行业知识
C语言——指针
���针的优点:
- 使程序更简洁、紧凑、高效
- 有效的表达更复杂的数据结构
- 动态分配内存
- 得到多于一个数的函数返回值
1. 指针的基本用法
1.1 指针的概念
-
内存地址:内存中每个字节单位都有一个编号(一般用十六进制表示)
-
指针:指针就是内存地址
-
指针变量:用于存放地址的变量就叫做指针变量
指针变量画图展示:
1.2 格式
存储类型 数据类型 *指针变量名;
int *p; //定义了一个指针变量p,指向的数据是int类型的。
int a = 5;int *p = &a;char c='v';char *q=&c;printf("%p %p\n", p, &a);printf("%p %p\n",q,&c);printf("%d %d\n",a,*p);printf("%c %c\n",c,*q);访问指针所指向空间的内容用取内容运算符*
那么p变量存放的就是a的地址,q变量存放的是c的地址。
符号*可以访问地址里面的内容。
指针与所指变量之间的关系如下图:
int i=3;
int *i_potinte=&i;
2.3 指针操作符
-
&:取地址符:取变量的地址
-
*:取内容符:取地址里面存放的内容
*&a=a;//*和&是互逆运算
&*a//错误(因为运算符优先级)
2.4 指针变量初始化和赋值
指针变量使用前不仅要定义,还要初始化。未初始化的指针变量不能随便使用,不然可能会产生野指针。
(1)将普通变量的地址赋值给指针变量
#include int main(int argc, char const *argv[]){ int a=10; int *p = &a;//定义的同时赋值 int *q=NULL;//可以初始化为空指针 int *p2; p2=p;//先定义后赋值 printf("%d %d %d\n",a,*p,*p2);//打印a的值 printf("%p %p %p\n",&a,p,p2); //打印a的地址 *p=20; //同过指向a的指针改变a的值 printf("%d %d %d\n",a,*p,*p2);//打印a的值 printf("%p %p %p\n",&a,p,p2); //打印a的地址 return 0;}(2)将数组的首地址赋值给指针变量
char s[10]="hello";char *p = s;int arr[5]={1,2,3,4,5};int *q=arr;printf("%c\n",*p);//hprintf("%d\n",*q);//1(3)将指针变量里面保存的地址赋值给另一个指针变量
int a=10; int *p=&a; int *q=p; printf("%d %d %d\n",a,*p,*q); *q=20;//通过指针改变变量a的值 printf("%d %d %d\n",a,*p,*q);p和q都指向了变量a
2. 指针运算
2.1 算术运算- +
对指针加减操作其实就是让指针向前向后移动
char str[32]="hello"; char *p=str; printf("%c\n",*p); p++; printf("%c\n",*p); p--; printf("%c\n",*p);int *p;p++;//移动4字节
double *q;q++;//移动8字节
p+n:访问高地址方向的第n个数据的地址,指针的指向不发生变化。
p-n:访问低地址方向的第n个数据的地址,指针的指向不发生变化。
偏移了多少字节=n*sizeof(数据类型)
2.2 关系运算 > >= p1"); else printf("p2);
练习:以下程序打印出来什么
char s[32]="hello";char *p=s;p++;cchar y=(*--p)++;printf("%c",y);//h练习:以下程序打印什么结果
int x[] = {10, 20, 30};int *px = x;printf("%d,", ++*px);//11printf("%d\n", *px); //11px = x;printf("%d,", (*px)++);//11printf("%d\n", *px);//12px = x+1;printf("%d,", *px++);//20printf("%d\n", *px);//30px = x+1;printf("%d,", *++px);//30printf("%d\n", *px);//30练习:下面程序段的运行结果是
char a[ ]=”language” , *p ;
p=a ;
while (*p!=’u’) { printf(“%c”,*p-32); p++ ; }
A)LANGUAGE B)language C)LANG D)langUAGE
C
3. 指针的大小和段错误
3.1 指针的大小
计算指针大小用:sizeof(指针变量名)
int a=5; short b=6; char c='a'; int *p1=&a; short *p2=&b; char *p3=&c; printf("%d %d %d\n",sizeof(p1),sizeof(p2),sizeof(p3));//4 4 4 4总结:
-
32位操作系统指针大小是4字节。64位操作系统,指针大小是8字节。
-
内存地址是固定的,但是变量的地址不是固定的。
-
指针类型根据指针指向的空间的数据类型来匹配
练习:
利用指针判断输入的字符串是否为回文
#include #include int main(int argc, char const *argv[]){ char a[32]={}; char *p1,*p2; int flag=1; gets(a); p1=a; p2=a+strlen(a)-1; while(p2>p1) { if(*p1!=*p2) flag=0; p1++; p2--; } if(flag==0) printf("no!\n"); else printf("yes!\n"); return 0;}练习:编写一个程序实现功能:将字符串“Computer Science”赋值给一个字符数组,然后从第一个字母开始间隔的输出该字符串,用指针完成。结果:Cmue cec
#include #include int main(int argc, char const *argv[]){ char string[32] = "Computer Science"; char *p = string; while (*p != '3.2 使用指针时报段错误
') { printf("%c", *p); p += 2; } printf("\n"); return 0;}int a=10; int *p=&a; scanf("%d",p); printf("%d\n",*p);段错误: Segmentation fault (core dumped)
(1)野指针,没有规定指向的指针会在内存中乱指。
野指针产生的原因,主要有两种:
【1】指针变量没有初始化
【2】指针被free之后,没有置NULL,会让人认为时合法指针。
这两种情况都是没有给指针规定指向。
例如:int *p;
scanf(“%d”, p);
printf(“%d\n”,*p);
修改:
#include #include int main(int argc, char const *argv[]){ char a[32]="123"; char *p=a; int num=0; while(*p != '4. 指针修饰
') { num = num*10+(*p-48); p++; } printf("num=%d\n",num); return 0;}(2)内存泄露,对非法空间进行赋值。
练习1.将字符串转换成整型数字输出。用指针实现
4.1 const 常量化
(1)修饰普通变量
const int a=10; int const b=20; int *p=&a,*q=&b; printf("%d %d\n",a,b);//10 20 *p=1; *q=2; //a=1;//error: assignment of read-only variable ‘a’ //b=2;//error: assignment of read-only variable ‘b’ printf("%d %d\n",a,b);//1 2(2) 修饰指针指向的内容
const int a=10;
int const b=10;
此时a和b只读,不可以修改。但是可以通过指针修改。
const int *p;int const *p;
#include int main(int argc, char const *argv[]){ int a=10; int b=20; int const *p=&a; const int *q=&b; printf("%d %d\n",a,b);//10 20 //*p=1;//*p只读不可以被更改 //*q=2;//*q只读不可以被更改 p=q; printf("%d %d\n",*p,*q);//20 20 return 0;}也就是修饰*p,此时指针指向的内容不能改变,但是指向可以改变。
#include int main(int argc, char const *argv[]){ int a=10; int *const p=&a; printf("%d\n",*p); //p=NULL;//error: assignment of read-only variable ‘p’ *p=20; printf("%d\n",*p); return 0;}4.2 void
(3) 修饰指针指向
int *const p;
此时const修饰p,指针指向不能被更改,但是指向的内容可以被更改。
int a=100;void *p=&a;int *q=(int *)p;printf("%d %d\n",*(int *)p,*q);4.3 大小端
(1) 不允许修饰普通变量:void a;//错误
(2) 可以修饰指针:void *p;
此时p是一个任意类型的指针
使用场景:函数参数或函数返回值。
注意:通过void类型修饰的指针进行取内容时,需要进行强制转换。
转换方式:(int *)p 为右值
#include #include int main(int argc, char const *argv[]){ int a=0x12345678; char *p=(char *)&a; if(*p==0x78) printf("LSB\n"); else if(*p==0x12) printf("MSB\n"); return 0;}现用现转,或者转换之后赋值给另外一个变量然后应用另外一个变量。
5. 二级指针
在计算机进行超过1字节数据进行存储时,会出现存储数据顺序不同的情况即大小端存储
-
大端:数据的低位存储在高地址位,数据的高位存储在低地址位,大端字节序称为MSB
-
小端:据的低位存储在低地址位,数据的高位存储在高地址位,小端字节序称为LSB
举例:存储数据:0x12345678, 假设起始地址是0x4000
地址:0x4000 0x4001 0x4002 0x4003
小端:0x78 0x56 0x34 0x12
大端:0x12 0x34 0x56 0x78
5.1 格式
a的值 -
一级指针:存放变量的地址
-
二级指针:存放一级指针的地址
a的地址 存储类型 数据类型 **指针变量名;
int a = 10;
int *p = &a;
int **q = &p;
此时p指向了a,q指向了p也就是q里保存了p的地址。访问a的值,a的地址和p的地址:
p的地址 a &a &p *p p q **q *q #include #include int main(int argc, char const *argv[]){ int a=10; int *p=&a; int **q=&p; printf("%d %d %d\n",a,*p,**q); printf("%p %p %p\n",&a,p,*q); printf("%p %p\n",&p,q); return 0;}10 10 100xbf9839b0 0xbf9839b0 0xbf9839b00xbf9839b4 0xbf9839b4
验证:
6.指针和一维数组
结果:
6.1 用法
#include int main(int argc, char const *argv[]){ int a[3]={1,2,3}; int *p=a; printf("%p %p %p %p\n",a,p,&a[0],&p[0]); printf("%p %p %p %p\n",a+1,p+1,&a[1],&p[1]); printf("%d %d %d %d\n",a[0],p[0],*a,*p); printf("%d %d %d %d\n",a[1],p[1],*(a+1),*(p+1)); return 0;}0xbfb27e90 0xbfb27e90 0xbfb27e90 0xbfb27e900xbfb27e94 0xbfb27e94 0xbfb27e94 0xbfb27e941 1 1 12 2 2 2
int a[5]={1,2,3,4,5}; //a是数组名,也是数组首地址。
int *p=a;
(1)直接访问:
(2)通过地址间接访问:
访问数组元素a[i]的地址:
-
直接访问:&a[i] 或 a+i
-
间接访问:&p[i] 或 p+i
访问数组元素a[i]的内容:
-
直接访问:a[i] 或 *(a+i)
-
间接访问:p[i] 或 *(p+i)
#include #include int main(int argc, char const *argv[]){ char str[32]=""; char *p1=str,*p2=NULL; int temp=0; gets(str); p2=p1+strlen(str)-1; while (p2>p1) { temp=*p1; *p1=*p2; *p2=temp; p1++; p2--; } printf("%s\n",str); return 0;}打印结果:
11 11 2 11 3 3 11 4 6 4 11 5 10 10 5 11 6 15 20 15 6 1
练习:用指针实现字符串“hello”的倒序打印。
#include int main(){ int a[10][10], i, j; /*负责对角线和每行第一列*/ for (i = 0; i练习:打印杨辉三角形前十行
-
-
-
-
-