从零开始的c语言:初步理解指针—从底层到入门(上)指针概念及语法,指针运算, 传地址和传值

从零开始的c语言---初步理解指针(概念详解)

前言:

距离上次更新博客已经过去了一个月,已经学了新知识,但是没有及时完成博客,这次我详细来讲述c语言的大关---------指针。

1. 内存和地址

1.1 内存

我们知道计算机上CPU(中央处理器)在处理数据的时候,需要的数据是在内存中读取的,处理后的数据也会放回内存中,那我们买电脑的时候,电脑上内存是8GB/16GB/32GB等,那这些内存空间如何⾼效的管理呢?

其实也是把内存划分为⼀个个的内存单元,每个内存单元的⼤⼩取1个字节。

计算机中常⻅的单位: ⼀个⽐特位可以存储⼀个2进制的位1或者0

中,每个内存单元,相当于⼀个学⽣宿舍,⼀个字节空间⾥⾯能放8个⽐特位,就好⽐同学们住的⼋⼈间,每个⼈是⼀个⽐特位。

1.2 地址

c 复制代码
#include <stdio.h>
int main() {
    int a = 10; // 变量a,存在某块内存里(比如地址是0x0012FF44)
    int *p = &a; // p是指针变量,存的是a的地址(&是"取地址符")
    
    printf("a的值:%d\n", a);       // 输出:a的值:10
    printf("a的地址:%p\n", &a);   // 输出:a的地址:0x0012FF44(具体地址依环境而定)
    printf("p存的地址:%p\n", p);  // 输出:p存的地址:0x0012FF44
    printf("p指向的值:%d\n", *p); // *是"解引用符",通过地址取对应的值 → 输出:10
    return 0;
}

2.指针语法

2.1 指针变量的定义

格式:类型 *指针变量名

类型:指针指向的变量的类型(比如int *p表示 p 指向的是 int 类型变量);

*:表示这是一个指针变量。

cpp 复制代码
// 不同类型的指针
int *p_int;   // 指向int的指针
char *p_char; // 指向char的指针
float *p_float;// 指向float的指针

2.2. 两个关键操作符

取地址符 &:获取变量的内存地址,赋值给指针变量;

解引用符 *:通过指针变量存的地址,访问对应的变量的值。

代码示例(修改指针指向的值):

c 复制代码
int a = 10;
int *p = &a;
*p = 20; // 通过指针修改a的值
printf("a = %d\n", a); // 输出:a = 20

指针的 "大小"
指针变量的大小,只和操作系统的 "地址位数" 有关(和指向的类型无关):
32 位系统:地址是 32 位 → 指针大小是 4 字节;
64 位系统:地址是 64 位 → 指针大小是 8 字节。

c 复制代码
#include <stdio.h>
int main() {
    int *p_int;
    char *p_char;
    printf("int指针大小:%zu字节\n", sizeof(p_int)); // 64位系统输出8
    printf("char指针大小:%zu字节\n", sizeof(p_char));// 64位系统输出8
    return 0;
}

2.3野指针:

1.指针未初始化:指向随机地址(可能导致程序崩溃)

c 复制代码
int *p; // 未初始化的野指针
*p = 10; 

解决:指针初始化时设为NULL(空指针),或直接指向有效变量。

2.指针类型不匹配:比如用char*指针指向int变量(可能导致数据截断)

c 复制代码
int a = 1000;
char *p = &a;  

2.4指针的基本运算

废话不多说,代码如下:

c 复制代码
#include <stdio.h>
int main() {
    int a = 10;
    int* p = &a;
    // 打印指针地址(十六进制)和指针+1后的地址
    printf("p的地址:%p\n", p);       // 例:0x7ffeefbff5c4
    printf("p+1的地址:%p\n", p+1);   // 例:0x7ffeefbff5c8(偏移4字节)
    
    char ch = 'a';
    char* pc = &ch;
    printf("pc的地址:%p\n", pc);     // 例:0x7ffeefbff5c3
    printf("pc+1的地址:%p\n", pc+1); // 例:0x7ffeefbff5c4(偏移1字节)
    return 0;
}

int类型的指针+1------------访问4个字节

char类型的指针+1------------访问1个字节

  1. 指针 - 指针:计算地址间的元素个数,只针对连续存放的数组,否则没有意义。

2.5const修饰指针

之前我们学过了const,但是为什么要在学一遍呢,const原本是为了变量不被修改所以才设置的,请看一下代码

c 复制代码
int main()
{
	const int i = 0;
	printf("i=%d\n", i);  //const修饰整型变量
	int* p = &i;
	*p = 9;
	printf("i=%d\n",*p);
	  
	return 0;
}

首先我们用const修饰了整型变量,但事实上i的值还是改变了,当我们用指针访问时,i仍旧会改变。

const指针的具体用法

const修饰的是它右边最近的内容------ 这是区分所有 const 指针的核心口诀。

指针指向的内容不能被修改(const 修饰指针指向的变量)

语法:const 类型* 指针名 或 类型 const* 指针名(两种写法等价)

规则:指针变量本身可以改(指向其他地址),但通过指针不能修改指向的内容。

c 复制代码
#include <stdio.h>
int main() {
    int a = 10, b = 20;
    const int* p = &a; // 或 int const* p = &a
    
    // *p = 30; → 错误:不能通过p修改a的值
    p = &b; // 正确:指针本身可以指向其他地址
    printf("*p = %d\n", *p); // 输出:*p = 20
    return 0;
}
  1. 指针变量本身不能被修改(const 修饰指针变量)
    语法:类型* const 指针名
    规则:指针变量本身不能改(不能指向其他地址),但通过指针可以修改指向的内容。
    代码示例:
c 复制代码
#include <stdio.h>
int main() {
    int a = 10, b = 20;
    int* const p = &a; // 指针p本身被const修饰
    
    *p = 30; // 正确:可以修改a的值
    // p = &b; → 错误:不能修改p的指向
    printf("a = %d\n", a); // 输出:a = 30
    return 0;
}
  1. 指针本身 + 指向的内容都不能被修改(const 同时修饰两者)
    语法:const 类型* const 指针名
    规则:指针既不能改指向,也不能通过指针修改内容。
c 复制代码
#include <stdio.h>
int main() {
    int a = 10, b = 20;
    const int* const p = &a;
    
    // *p = 30; → 错误:不能改内容
    // p = &b; → 错误:不能改指向
    printf("*p = %d\n", *p); // 输出:*p = 10
    return 0;
}

3,传值调用和传址调用

传地址:

c 复制代码
void reverse(int* x, int* y)//传入指针变量
{
	int temp = 0;
	temp = *x;
	*x = *y;
	*y = temp;//指针交换
}

int main()
{
	int a = 20;
	int b = 10;
	printf("交换前的a=%d,b=%d\n", a, b);//20 10
	reverse(&a, &b);//实参地址
	printf("交换后的a=%d,b=%d\n", a, b);//10 20
	return 0;
}

传值调用

c 复制代码
void change(int x) {
    x = 100; // 修改的是x的副本
}
int main() {
    int a = 10;
    change(a);
    printf("a = %d\n", a); // 输出:a = 10(原变量没被修改)
    return 0;
}

如图所示,传值和传地址是不同的,这是为什么呢,如果是传值,改变形参的变量,实参是不会改变的,形参只是实参的临时拷贝,所以不会影响到是实参。

传地址:通过指针的访问,传入实参的地址,通过指针间接访问实参修改了值。

4.易错点和 "坑"

(*p)++:先取解引用后的值,在加加;

*p++:先取 p 指向的值,再将 p 后移 1 位;

*++p:先将 p 后移 1 位,再取新地址的值。

越界访问:指针偏移超出数组 / 内存边界,会访问到非法内存

c 复制代码
#include<stdio.h>
#include<assert.h>
#include<string.h>
int main()
{
	int arr[10] = { 0 };
	int* p = &arr[0];
	int i = 0;
	for (i = 0; i <= 11; i++)
	{
		*(p++)=i;//指针越界访问
	}
	p = NULL;
	 
	assert(p != NULL);//括号里面的是条件

	return 0;
}
c 复制代码
////实现strlen功能
int my_strlen(char* p)
{
	int count = 0;
	assert(p != NULL);
	while (*p)//遍历数组的字符=*p!='\0'
	{
		count++;
		p++;
		 
	}
	return count;
	 
}
 
int main()
{
	char arr[] = "abcdef";
	int len=my_strlen(arr);
	printf("%d", len);
	
	return 0;
 
 }


不积跬步,无以至千里。

相关推荐
cyforkk25 分钟前
12、Java 基础硬核复习:集合框架(数据容器)的核心逻辑与面试考点
java·开发语言·面试
我材不敲代码4 小时前
Python实现打包贪吃蛇游戏
开发语言·python·游戏
身如柳絮随风扬5 小时前
Java中的CAS机制详解
java·开发语言
韩立学长7 小时前
【开题答辩实录分享】以《基于Python的大学超市仓储信息管理系统的设计与实现》为例进行选题答辩实录分享
开发语言·python
froginwe117 小时前
Scala 循环
开发语言
m0_706653237 小时前
C++编译期数组操作
开发语言·c++·算法
故事和你918 小时前
sdut-Java面向对象-06 继承和多态、抽象类和接口(函数题:10-18题)
java·开发语言·算法·面向对象·基础语法·继承和多态·抽象类和接口
嵩山小老虎8 小时前
Windows 10/11 安装 WSL2 并配置 VSCode 开发环境(C 语言 / Linux API 适用)
linux·windows·vscode
Bruk.Liu8 小时前
(LangChain实战2):LangChain消息(message)的使用
开发语言·langchain
qq_423233908 小时前
C++与Python混合编程实战
开发语言·c++·算法