写点什么

【C 语言】指针总结,Java 程序员如何有效提升学习效率

作者:Java高工P7
  • 2021 年 11 月 10 日
  • 本文字数:2421 字

    阅读完需:约 8 分钟

int a = 10;


int* p1 = &a; //正确,把 a 的内存地址赋值给 p1


int* p2; //错误,未初始化,若直接进行访问,会报错


return 0;


}


b. 指针越界访问,如下


int main()


{


int arr[10] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};


int* p = arr; //数组名,首元素地址


for (int i = 0; i < 11; i++)


{


printf("%d ",*(p+i)); //数组元素共 10 个,最后一个元素的下标是 9,i 变量能够等于 10,越界


printf("%d ",arr[i]); //此处两个 printf 函数的作用,等价


}


return 0;


}


c.指针指向的内存空间已经被释放,如下


struct Student


{


char name[20];


int age;


};


int main()


{


//创建一个结构体,返回的是指向 struct Student 类型的指针变量 s1


struct Student* s1 = (struct Student*)malloc(sizeof(struct Student));


//假设现在我们不需要这块空间了,在 C/C++中,malloc 申请的内存空间需要程序员自己手动释放(回收)


free(s1);


//现在 s1 已经被释放(回收)了,意思是 由变量 s1 指向的这块内存空间还给了系统


//但是 s1 里面还是保存着这块内存空间(struct Student)的内存地址(可理解为房间号)


//如果此时再次去访问这块空间,那就会出错的。因为这块空间已经回收了


s1->name = "博尔特"; //错误,内存空间已经回收,这也称为也指针


return 0;


}

3、如何规避上诉问题?

有人说:“不使用指针,就完美的规避了”。 那不可能啊,衡量一个 C/C++程序员的技术水平,指针的运用占了不小的份额。


  • 指针初始化

  • 小心指针越界访问

  • 指针指向的空间释放后,及时置为 NULL

  • 指针使用之前检查有效性


三、六大基本指针类型



1、char*

char ch = 'A';


char* p = &ch; //将 ch 的内存地址赋值给 p

2、short*

short a = 1314;


short* p = &a; //将 a 的内存地址赋值给 p

3、int*

int a = 10;


int* p = &a; //将 a 的内存地址赋值给 p

4、long*

long a = 100000;


long* p = &a; //将 a 的内存地址赋值给 p

5、float*

float a = 3.0;


float p = &a; //将 a 的内存地址赋值给 p

6、double*

double a = 5.0;


double* p = &a; //将 a 的内存地址赋值给 p


四、指针运算



1、& 与 *

&: 取地址符,可以取出变量在内存中的地址。


示例: &number, 取出 number 变量的内存地址


**:**间接访问操作符,也叫解引用,不要与二元运算符()混淆,二者符号相同,但语法不同。


int main()


{


int a = 20;


int* p = &a; //取 a 的地址


printf("%d\n",*p); //通过指针 p,进行解引用,去输出 a 的值


return 0;


}

2、指针 ± 整数

int main()


{


char arr1[5] = {'a','b','c','d','e'};


int arr2[5] = {1, 2, 3, 4, 5};


char* p1 = arr1;


int* p2 = arr2;


for (int i = 0; i < 5; i++)


{


printf("%c ",*(arr1 + i));


}


for (int i = 4; i >= 0; i--)


{


printf("%d ", *(arr2 + i));


}


return 0;


}


上诉代码输出的结果是


a b c d e



5 4 3 2 1


char 类型的变量占一个字节的空间,int 类型的变量占四个字节的空间。而两个指针变量 p1 和 p2 也能够加减一个整数去读取内存中的数据。


也就是说,int* 的指针 p2 + 1 ,并不是向后访问一个字节空间,而是访问的 int 类型的 4 个空间。例如:short* 的指针变量 p+1,向后访问的是 short 类型的 2 个空间,如下图 int*类型指针加减的过程


[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-V4KIv7Zh-1622041026120)(asserts/%E5%8A%A8%E7%94%BB.gif)]

3、指针 - 指针

int my_strlen(char *s) {


char *p = s;


while(*p != '\0' )


p++;


return p-s;


}


int main()


{


int a = 10;


int b = 20;


int* p1 = &a;


int* p2 = &b;


printf("%d\n", p2 - p1);


printf("%d\n", p1 - p2); 高地址 减 低地址 再+1,得到的是两块内存空间之间的内存空间个数


return 0;


}

4、const int* p 与 int* const p

int a = 10;


const int* p = &a;


int* const p = &a;


第一个:const int* p = &a; const 关键字是修饰的是 *p,如下图:




第二个:int* const p = &a;这次 const 关键字修饰的 p,而不是*p。如图




综上所诉,谁写在 const 的后面就是修饰谁,谁就不能改动。


const int* p = &a; 此时 *p = 20; 不可取 ,* p 被修饰



int* const p = &a; 此时 p = &b; 不可取,p 被修饰


五、指针与数组



1、指针数组

中心意思是一个数组,这个数组里面的每一个元素是指针类型的变量


int a = 10;


int b = 20;


int* arr[2] = { &a, &b}; //这就是一个指针数组

2、数组指针

中心意思是一个指针,这个指针指向的是一个数组的空间。


//第一种情况


int arr[3][3] = {{1,2,3},{3,4,5},{5,6,7}};


int (*parr)[3] = arr; //此处的 arr 是二维数组的首元素地址,这个地址是{1,2,3}这个一维数组的地址


//第二种情况


int arr2[3] = {7,8,9};


int (*parr2)[3] = &arr; //此处取出的是 arr 这整个数组的地址,注意区分二者

3、一维数组传参

#include <stdio.h>


void test(int arr[])//ok


{}


void test(int arr[10])//ok


{}


void test(int *arr)//ok


{}


void test2(int *arr[20])//ok


{}


void test2(int **arr)//ok


{}


int main()


{


int arr[10] = {0};


int *arr2[20] = {0};


test(arr);


test2(arr2);


}

4、二维数组传参

void test(int arr[3][5])//ok


{}


void test(int arr[][])//ok


{}


void test(int arr[][5])//ok


{}


//总结:二维数组传参,函数形参的设计只能省略第一个[]的数字。


//因为对一个二维数组,可以不知道有多少行,但是必须知道一行多少元素。


//这样才方便运算。


void test(int *arr)//不 ok


{}


void test(int* arr[5])//不 ok


{}


void test(int (*arr)[5])//ok


{}


void test(int **arr)//不 ok


{}


int main()


{


int arr[3][5] = {0};


test(arr);


}


不管是一维数组传参还是二维数组传参,归根结底还是要弄清楚,所传递过去的参数是什么,特别需要区分数组,arr 和 &arr,一维还是二维。二者还是有一定的区别。


六、函数指针




先看一段代码。


#include <stdio.h>


void test()


{


printf("hehe\n");


}


int main()


{


printf("%p\n", test);


printf("%p\n", &test);


return 0; }



两种 printf 函数输出的就是 test 函数的地址,通过这个地址可以去调用这个函数。


那如何保存呢??


void (*ptest)(void) = &test; //*先和 ptest 结合,说明是一个指针,往外看,后面是个(void)


//说明是一个函数,这就是函数指针

用户头像

Java高工P7

关注

还未添加个人签名 2021.11.08 加入

还未添加个人简介

评论

发布
暂无评论
【C语言】指针总结,Java程序员如何有效提升学习效率