C语言-指针 àì夳堔傛蜴生んèń 2022-01-26 11:17 377阅读 0赞 ## 1.指针和指针变量 ## > * **内存区的每一个字节都有编号,这就是地址** > * **如果在程序中定义了一个变量,在对程序进行编译和运行时,系统就会给这个变量分配内存单元,并确定它的内存地址(编号)** > * **指针的实质就是内存'地址',指针就是地址,地址就是指针** > * **指针是内存单元的编号,指针变量是存放地址的变量** > * **通常我们叙述时会把指针变量简称指针,实际上它俩含义不相同** > * **在指针声明时,\*号表示所声明的变量为指针** > * **在指针使用时,\*号表示操作指针所指向的内存空间** > > 1. **\*相当于通过地址(指针变量的值)找到指针所指向的内存,再操作内存** > 2. **\*放在等号的左边赋值(给内存赋值,写内存)** > 3. **\*放在等号的右边取值(从内存中取值,读内存)** ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxNDgxOTI0_size_16_color_FFFFFF_t_70][] **指针步长** > **指针步长:指针+1要向后跳动的字节** > > **指针的类型不单单决定的步长,还决定解引用的时候从给定地址开始取类型大小的字节数** #include <stdio.h> #include <stddef.h> //计算偏移量 struct stu { int a; //4 char b; //1,内存对齐后为4 char arr[20]; //20 int c; //4 }; int main() { char* p = NULL; printf("%d\n",p); printf("%d\n",p+1); int* p1 = NULL; printf("\n%d\n",p1); printf("%d\n",p1+1); struct stu p2 = {10,'A',"hello world",20}; //&p+1 直接跳过结构体 printf("a off = %d\n",offsetof(struct stu,b)); //求出b相对于结构体的偏移量 4 printf("c off = %d\n",offsetof(struct stu,c)); //求出c相对于结构体的偏移量 28 printf("c = %d\n",*(int *)((char*)&p2+offsetof(struct stu,c))); //p2的地址+c的偏移量再以步长为4解引用获取c的值 } ![20190615125536612.png][] **指针变量的定义和使用** > * **指针也是一种数据类型,指针变量也是一种变量** > * **指针变量指向谁,就把谁的地址赋给指针变量** > * **"\*"操作符操作的是指针变量指向的内存空间** #include <stdio.h> int main() { int a = 0; char b = 100; printf("%p\t%p\n",&a,&b);//打印a,b的地址 int* p; //int* 是一种数据类型,int*是指针类型,p才是变量名 p = &a; //定义了一个int类型的指针变量,可以指向一个int类型变量的地址 printf("a的值:%d\n",*p); //p指向了a的地址,*p就是取a的值 char* p1 = &b; printf("b的值:%c\n",*p1); //p1指向b的地址,*p1就是取b的值,d对应的ASCII码值是100 return 0; } ![20190529112142585.png][] **注**:&可以取得一个变量在内存中的地址。但是,不能取寄存器变量,因为寄存器变量不在内存里,而在CPU里面,所以是没有地址的。 **指针大小** > * 使用sizeof()测量指针的大小,得到的总是:4或8 > * sizeof()测的是指针变量指向存储地址的大小 > * 在32位平台,所有的指针(地址)都是32位(4字节) > * 在64位平台,所有的指针(地址)都是64位(8字节) **野指针** > **定义**:野指针是指向一个未知的内存空间,可能在读写的时候发生错误 > > **注**:指针变量也是变量,是变量就可以任意赋值,不要越界即可,但是,**任意数值赋值给指针变量没有意义**,因为这样的指针变成了**野指针**,此指针指向的区域是未知(**操作系统不允许操作此指针指向的内存区域**)。所以,野指针不会直接引发错误,操作野指针指向的内存区域才会报错。 > > **什么情况下会导致野指针?** > > 有时指针在free或delete后未赋值NULL,便会使人以为是合法的。别看free和delete的名字,他们只是把指针所指的内存给释放掉,但并没有把指针本身干掉。此时指针指向的就是'垃圾'内存。释放后的指针应立即将指针置为NULL,防止产生野指针 > > 不要返回指向栈内存的指针或引用,因为栈内存在函数结束后会被释放 > > 任何指针变量刚被创建时不会自动成为NULL指针,它的缺省值是随机的,它会乱指一气。所以,指针变量在创建的 同时应该被初始化,要么将指针设置为NULL,要么让它指向合法的内存 > > * **指针释放后未置空** > * **指针操作超越变量作用域** > * **指针变量未初始化** #include <stdio.h> int main() { int a = 20; //0-255的内存空间都是系统保留的,不能读也不能写 int* p = &a; //定义指针变量p指向a的地址 p = 100; //使p指向一个内存编号为100的未知空间 *p = 100; //对未知空间进行赋值操作 printf("%d\n",*p); return 0; } **空指针** > **定义**:空指针就是指向内存编号为0的空间,操作该内存空间会报错,一般情况空指针用作条件判断 **const修饰指针** **1.通过指针可以修改const修饰的常量** #include <stdio.h> int main() { const int a = 10; int* p = &a; *p = 20; printf("%d\n",a); printf("%d",*p); } ![20190530072817538.png][] ** 2.利用const修饰指针类型(int\*)可以改变指针指向的地址,不能改变指针指向内存地址的值** #include <stdio.h> int main() { int a = 10,b = 20; const int* p ; p = &a; printf("%d\n",a); printf("%d\n",*p); p = &b; //用const修饰指针类型(int*),只能改变指针指向的地址,不能改变指针指向内存地址的值 // *p = 100; //不能改变 printf("%d\n",b); printf("%d\n",*p); } ![20190530073242503.png][] **3.使用const修饰指针变量,不能改变指针指向,可以改变指针指向内存地址的值** #include <stdio.h> int main() { int a = 10,b = 20; //使用const修饰指针变量,不能改变指针指向,可以改变指针指向内存地址的值 int* const p = &a; //p = &b; //不能改变指针指向 *p = 100; printf("%d\n",a); printf("%d\n",*p); } ![20190530073953245.png][] 4.const修饰指针类型(int\*),const修饰指针变量,既不能改变指向地址,也不能改变指向地址的值 #include <stdio.h> int main() { int a = 10,b = 20; const int* const p = &a; //*p = 100; const修饰int*,不能改变指针指向内存地址的值 //p = &b const修饰指针变量p,不能改变指针指向地址 printf("%d\n",a); printf("%d\n",*p); } ![20190530074718993.png][] **表格表示:** ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxNDgxOTI0_size_16_color_FFFFFF_t_70 1][] ## 2.指针与数组 ## **不同的打印方式:若p=arr;** * **\*p++ 等同于\*(p++)先取p指向的内容再使p指向下一个地址,则p初始指向arr【0】** * **\*++p等同于\*(++p)先让p指向下一个地址再获取p指向的内容****,则p初始指向arr【1】** * **p\[i\] 等同于arr\[i\],若p初始指向arr\[1\],则p\[2\]实际为arr\[3\],而一般情况下p\[2\]为arr\[2\],所以使用这种方式易造成数组溢出** * **\*(p+i)等同于让p加上i\*sizeof(数据类型\*)后,再获取指向的内容** #include <stdio.h> int main() { //数组名是数组的首地址,这是一个常量 int arr[] = {1,2,3,4,5,6,7,8,9,10}; int* p = arr; //[数据类型 *] 变量名 //p = arr for(int i=0;i<10;i++){ //p++ 每次加sizeof(int*)个字节到p //printf("%d\n",*p++); //printf("%d\n",p[i]); //内存每次变动i*sizeof(int *)位 //printf("%d\n",*(p+i)); printf("%d\n",*(arr+i)); } } **指针实现冒泡排序** #include <stdio.h> void bubblesort(int *p,int n); int main() { int n; printf("请输入创建数组的长度:\n"); scanf("%d",&n); int arr[n]; int *p = arr; printf("请输入数组元素:\n"); for(int i=0;i<n;i++){ scanf("%d",&arr[i]); } printf("排序前:\n"); for(int i=0;i<n;i++){ printf("%d\t",arr[i]); } printf("\n排序后:\n"); bubblesort(arr,n); for(int i=0;i<n;i++){ printf("%d\t",*p++); } } void bubblesort(int *p,int n) { int temp; for(int i=0;i<n-1;i++){ for(int j=0;j<n-i-1;j++){ if(*(p+j)>*(p+j+1)) //arr[j] >arr[j+1] { temp = *(p+j); *(p+j) = *(p+j+1); *(p+j+1) = temp; } } } } ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxNDgxOTI0_size_16_color_FFFFFF_t_70 2][] **指针实现strchr函数** #include <stdio.h> char *mystrchr(const char *arr,char ch); int main() { int n; printf("请输入创建数组的长度:\n"); scanf("%d",&n); char c,ch,arr[n],*p = arr;; printf("请输入字符串:\n"); c = getchar(); scanf("%[^\n]",&arr); printf("请输入查询的字符:\n"); scanf(" %c",&ch); if(p!= NULL){ p = mystrchr(arr,ch); printf("%s\n",p); } return 0; } char *mystrchr(const char *arr,char ch) { char *p = arr; while(*p != '\0'){ if(*p == ch){ return p; } p++; } printf("没找到!\n"); return NULL; } ![2019053107405455.png][] **字符串逆转** #include <stdio.h> void reverse(char *arr,int n); int main() { int n; printf("请输入字符串长度:\n"); scanf("%d",&n); char arr[n],c; c = getchar(); //防止程序将上一个输入的结束输入回车符当做下一个的输入 printf("请输入字符串:\n"); gets(arr); printf("\n反转后:\n"); reverse(arr,n); puts(arr); } void reverse(char *arr,int n) { char* p1 = arr; //定义指针p1指向arr char* p2 = &arr[n-1]; //p2指向arr的最后一个元素 while(p1<p2) //因为地址连续,直接用地址进行比较 { char temp = *p1; *p1 = *p2; *p2 = temp; p1++; p2--; } } ![20190602092239471.png][] **指针数组** #include <stdio.h> int main() { int a=10,b=20,c=30; int* arr1[] = {&a,&b,&c}; //指针数组既是指针也是数组,数组中保存的是地址 char* arr2[] = {"hello","world","1234567"}; //数组中实际存储的是三个字符串的地址 for(int i=0;i<3;i++){ printf("%d\t",*arr1[i]); } for(int i=0;i<3;i++){ printf("%c\t",*(arr2[i]+1)); //打印每个字符串第二个数 } return 0; } ![20190602102623984.png][] **多级指针** #include <stdio.h> int main() { int a = 10; int* p1 = &a; //二级指针 前面加*代表一级指针的值(地址) p1 //二级指针 前面加**代表一级指针指向地址的值 a //*p2 = p1 = &a //**p2 = *p1 = a; int** p2 = &p1; //三级指针 //*p3 = p2 = &p1 //**p3 = *p2 = p1 = &a //***p3 = **p2 = *p1 = a int*** p3 = &p2; printf("a的地址:\t%p\n\n",a); printf("p1的地址:\t%p\n",p1); printf("p2的地址:\t%p\n",p2); printf("p3的地址:\t%p\n\n",p3); printf("*p2的地址:\t%p\n",*p2); printf("**p2的地址:\t%p\n\n",**p2); printf("*p3的地址:\t%p\n",*p3); printf("**p3的地址:\t%p\n",**p3); printf("***p3的地址:\t%p\n",***p3); } ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxNDgxOTI0_size_16_color_FFFFFF_t_70 3][] **指针和函数** #include <stdio.h> void swap1(int a,int b); void swap2(int* a1,int* b1); int main() { int a = 10,b = 20; swap1(a,b); printf("方式1执行后:\n%d\t%d\n",a,b); swap2(&a,&b); printf("方式2执行后:\n%d\t%d\n",a,b); return 0; } void swap1(int a,int b) //值传递,传递是a,b的副本,函数执行完成后会自动销毁分配的内存空间 { int temp; temp = a; a = b; b = temp; } void swap2(int* a1,int* b1) //地址传递,直接操作a,b的值 { int temp; temp = *a1; *a1 = *b1; *b1 = temp; } ![20190603211114929.png][] **函数返回值为指针** #include <stdio.h> char* fun() { //字符数组 创建位置在栈区 函数执行完成后销毁 //char arr1[] = "hello world"; //字符串常量 创建位置在常量区 程序运行结束后销毁 可以读取不能修改 char* p = "hello world"; return p; //返回指针p指向的地址 } int main() { char* p = fun(); printf("%p\n",p); printf("%s\n",p); return 0; } ![20190603213408201.png][] #include <stdio.h> int main() { char* arr = "hello world"; //保存在常量区 char arr1[] = "hello world";//保存在栈区 char* p = arr; printf("%s\n",p); printf("%c\n",p[0]); printf("%p\n",arr); //p = "hello world"; //指向位于常量区的字符串 //字符串常量是一个常量的数组 可以读取字符或字符串 不能修改 //p[0] = 'A'; //*p = 'A'; printf("--------------\n"); p = arr1; //p指向arr1,可以修改值 p[0] = 'A'; //修改'h'为'A' p++; //p指向下一个字符 *p-- = 'B'; //相当于*(p--),先取p的值并修改为'B',再使p指向上一个字符 printf("%s\n",p); return 0; } ![20190603221535768.png][] **字符串排序** #include <stdio.h> //1.以二级指针形式 void bubble(char **arr,int len) { for(int i=0;i<len-1;i++){ for(int j=0;j<len-i-1;j++){ //因为arr是二级指针,*arr指向每个字符串的首地址,**arr取每个字符串的首字母 if(**(arr+j)>**(arr+j+1)){ //若满足比较,则将二者的地址互换 char* temp = *(arr+j); *(arr+j) = *(arr+j+1); *(arr+j+1) = temp; } } } } //2.以二维数组形式 void bubble1(char** arr,int len) { for(int i=0;i<len-1;i++){ for(int j=0;j<len-i-1;j++){ //*(arr+j) = arr[j],**(arr+j)=arr[j][0] if(arr[j][0]>arr[j+1][0]){ char *temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } } //3.一半数组一般指针 void bubble3(char** arr,int len) { for(int i=0;i<len-1;i++){ for(int j=0;j<len-i-1;j++){ if(*arr[j]>*arr[j+1]){ char *temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } } int main() { char* arr[] = {"aaaa","cccc","ffff","dddd"}; bubble1(arr,4); //使用冒泡排序,对每个字符串首字母进行比较,按ASCII码从小到大 for(int i=0;i<4;i++){ printf("%s\n",arr[i]); } } ![20190604100619205.png][] [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxNDgxOTI0_size_16_color_FFFFFF_t_70]: /images/20220126/04434ba080a5481ca6593f2c4f139dd6.png [20190615125536612.png]: /images/20220126/9626c519edaa41b789f3140f40d0bfed.png [20190529112142585.png]: /images/20220126/2bfdf3927662435eb536ba8b9c907250.png [20190530072817538.png]: /images/20220126/450e9e8466414a50807f60a3020c5c45.png [20190530073242503.png]: /images/20220126/c15b25c51bc84a60b1d173d806a3567c.png [20190530073953245.png]: /images/20220126/7c5776390278456fb9e517bb6231f692.png [20190530074718993.png]: /images/20220126/2a3ea8459f824f17b5a7d360cd2df539.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxNDgxOTI0_size_16_color_FFFFFF_t_70 1]: /images/20220126/c8af3228a8c34601bad6f56d06a27ecf.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxNDgxOTI0_size_16_color_FFFFFF_t_70 2]: /images/20220126/a157707796d0424d82ea78c651abf29e.png [2019053107405455.png]: /images/20220126/ca723eb77aa04ff38af2f1c8c2435a78.png [20190602092239471.png]: /images/20220126/e5a7873da4fc48c58a7432a0973dca6f.png [20190602102623984.png]: /images/20220126/38a12526ec7e461095ae90d9a9e40aa2.png [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzQxNDgxOTI0_size_16_color_FFFFFF_t_70 3]: /images/20220126/f908354031b142e78291da24405ce86e.png [20190603211114929.png]: /images/20220126/2e4dc76b320344279102b150e98e95a6.png [20190603213408201.png]: /images/20220126/d5fc383e86a449998d323804673b4a4e.png [20190603221535768.png]: /images/20220126/34952d5e4cfc49178fc88ece2b8be597.png [20190604100619205.png]: /images/20220126/9a8f42beac194d23aea732bd0a6abd96.png
相关 C语言指针 C语言指针 1、 指针概述: 1、指针是一种数据类型,占用内存空间。用来保存内存地址。 2、空指针: 标准定义了NULL指针,它作为一个特殊的指针变量, Bertha 。/ 2023年05月31日 07:56/ 0 赞/ 3 阅读
相关 【C语言】指针 50、指针变量用来记录地址数据 51、只有记录了有效地址的指针变量才可以使用 52、声明指针变量时需要在变量名称前写\ 53、指针变量名称前加\可以表示指针所捆绑的存储区 亦凉/ 2022年06月12日 04:49/ 0 赞/ 266 阅读
相关 c语言指针 //指针怎么用 // (区分两个概念,指针变量和指向内容,指针变量只保存一个地址值,在这个地址值对应的内存中具体存放的东西叫做指向内内容) 用变量 ╰半夏微凉°/ 2022年06月04日 08:58/ 0 赞/ 280 阅读
相关 C语言指针 C语言中,指针尤为重要。 1可以把指针看作是一个数据类型。 ![SouthEast][] 打印结果: ![SouthEast 1][] 2 在指针声 今天药忘吃喽~/ 2022年05月31日 03:39/ 0 赞/ 270 阅读
相关 C 语言指针 1、CPU 访问内存,是通过内存地址来读写内存数据的,CPU 与内存条硬件之间有个地址总线,CPU 通过地址总线将要访问/写入的内存地址告诉内存条。 2、对于 CPU 而言地 拼搏现实的明天。/ 2022年05月29日 01:54/ 0 赞/ 305 阅读
相关 C语言指针 1、指针的基本知识 (1)定义 int \p; (2)赋值 1) int a;int p=&a 2) int a ;int p; 喜欢ヅ旅行/ 2022年03月26日 05:22/ 0 赞/ 316 阅读
相关 C语言:指针 今天研究了一下午终于把指针研究明白了 include<stdio.h> include<stdlib.h> int main() { 快来打我*/ 2022年03月25日 21:14/ 0 赞/ 330 阅读
相关 C语言-指针 1.指针和指针变量 > 内存区的每一个字节都有编号,这就是地址 > 如果在程序中定义了一个变量,在对程序进行编译和运行时,系统就会给这个变量分配内存单元,并确 àì夳堔傛蜴生んèń/ 2022年01月26日 11:17/ 0 赞/ 378 阅读
相关 C语言指针 变量的地址 我们已经知道,内存变量简称变量,在C语言中,每定义一个变量,系统就会给变量分配一块内存,而内存是有地址的。如果把计算机的内存区域比喻成一个 小咪咪/ 2021年11月10日 23:06/ 0 赞/ 489 阅读
还没有评论,来说两句吧...