C语言指针深入 I 学习总结
2026/7/23 3:39:57 网站建设 项目流程

目录

1.const修饰指针

2.野指针

3.assert断言

4.传值调用和传址调用


一.const修饰指针

例1.const修饰局部变量aa不能够被赋值;

const修饰局部变量aa 是常变量:本质依旧是变量,拥有内存空间,但是通过变量名 a 不能直接修改里面的值a = 100;,尝试直接对 const 常变量赋值。

//const int main() { const int a = 10;//被const修饰后,变成了一个常变量 -具有常属性的变量(不能被修改了)骨子里还是一个变量 a = 100; printf("%d\n", a); return 0; }

代码运行结果:

解说:试图修改只读变量a,程序无法生成可执行文件,跑不起来。

例2:数组大小里要存放常数,不能使用常变量

接下来让我们了解两个概念:常量与长变量解释

  1. 常量:编译期就确定的值;

  2. 常变量const int n,运行时才有内存,不属于编译常量

int main() { //const修饰变量时,不是常量,是一个变量 const int n = 10;//c99之前,这个n不能放在数组的大小指定的位置; int arr[n]; arr[0] = 1; return 0; }

代码运行结果:

解说:数组长度必须是编译期常量,n是常变量,编译报错

例3.const 只禁止直接修改变量名,没有锁死内存;借助指针获取地址,可以间接修改局部 const 变量里面的值。

int main() { //打破const常变量 const int a = 10; int* p = &a; *p = 100; printf("%d\n", a);//输出的是100不是10,就好比你进一个房子里,有门可以进,也有窗户可以进,当使用const修饰变量后,就相当于把门锁了,只能从窗户进去 return 0; }

代码运行结果:

解说:不能直接修改 const 修饰的 a,但借助指针可以绕开限制,间接修改 a 存储的数据;这种方式不一定在所有环境都能用。

例4.const修饰指针变量
1. 放在*的左边:const修饰的指针变量指向的内容,不能通过指针变量修改它指向的内容,但是指针变量的指向是可以修改的;
2.放在*的右边:const修饰的指针变量本身,指针变量的指向是不能修改的,但是指针指向的内容,是可以通过指针来改变;

int main() { int n = 10; const int* p = &n;// 指向常量的指针:*p只读,p可以换指向 int m = 100; p = &m;// p里面的值被覆盖成(内存B的地址),p现在指向m printf("%d\n", n);//10 return 0; }
int main() { int n = 10; int* const p = &n;// p存放n的地址;p从此以后不能再指向别的变量 *p = 100;// *p等价于n,修改n内存里的值 printf("%d\n", n);//100 return 0; }

二.野指针

就是指针指向的位置是不可知的(随机的,不正确的,没有明确限制的).

造成野指针的有三种情况:

1.指针未初始化

2.指针访问越界

3.指针指向的空间释放

例5.列举局部变量与全局变量未初始化,代码会怎样运行。

//全局变量不主动初始化的情况下,编译器默认会给初始化0; int g_val; int main() { //局部变量不初始化,里边的值是不可预测的,可能是随机值;如果在初始化前加上static,编译器默认会给初始化0; //int n; //printf("%d\n", n);//编译失败 static int n; printf("%d\n", n);//如果没有加static,将编译失败;加static输出0; printf("%d\n", g_val);//0 return 0; }

代码运行结果:

解说:

  • 全局变量不加初始化,程序加载时默认初始化为 0;

  • 被 static 修饰的局部静态变量,同样默认初始化为 0;

  • 普通局部变量不初始化,里面存放随机垃圾值。

例6.指针未初始化

//1.指针未初始化 int main() { //int n = 10; //int* p = &n;//指针初始化 int* p; *p = 10;//p就是野指针 return 0; }

例7.指针越界访问

//2.指针越界访问 int main() { int arr[10] = { 0 }; int* p = &arr[0]; int i = 0; for (i = 0;i <= 11;i++) { *(p++) = i;//先执行*p,在执行++; } return 0; }

例8.指针指向的空间释放

//3.指针指向的空间释放 int* test() { int n = 100; return &n; } int main() { int* p = test(); printf("%d\n", *p);//*p为野指针 return 0; } int main() { int n = 10; int* p = &n; int* p1 = NULL; //NULL专门用来初始化指针变量,chushihuaweiNULl的指针变量,意味着:这个指针没有可以指向有效空间的地址,我们暂时不能使用它 return 0; }

三.assert断言

  • assert(条件)断言宏。条件为真,程序正常运行;条件为假,程序直接崩溃,打印出错文件、行号。

  • #define NDEBUG必须写在#include <assert.h>前面,开启后所有 assert 全部失效。

例9

//#define NDEBUG #include <assert.h> //assert断言 int main() { //int* p = NULL; int n = 10; //int* p = &n; int* p = NULL; assert(p != NULL); printf("%d\n", *p); return 0; } //strlen #include "assert.h" size_t my_strlen(const char *p) { int count = 0; assert(*p != NULL); while (*p != NULL) { count++; p++; } return count; } int main() { char arr[] = "abcdef"; size_t len = my_strlen(arr); printf("%zu\n", len); return 0; }

四.传值调用和传址调用

例10

//传值调用 //实参传递给形参的时候,形参是实参的一份临时拷贝 //对形参的修改不影响实参; void swap(int x, int y) { int z = x; x = y; y = z; } int main() { int a = 0; int b = 0; scanf("%d %d", &a,&b); printf("交换前:a = %d b = %d\n", a, b); swap(a, b); printf("交换后:a = %d b = %d\n", a, b); return 0; }

例11

//传址调用 void swap(int *x, int *y) { int tmp = *x; *x = *y; *y = tmp; } int main() { int a = 0; int b = 0; scanf("%d %d", &a, &b); printf("交换前:a = %d b = %d\n", a, b); swap(&a, &b); printf("交换后:a = %d b = %d\n", a, b); return 0; }

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询