目录
指针是什么?
总结
指针和指针类型
指针类型的意义
总结
总结2
野指针
一般情况下,如何避免野指针
指针运算
指针+-整数
指针-指针
用指针实现strlen函数
指针的关系运算
指针和数组
二级指针
关系图
指针数组
指针是什么?
指针是内存中一个最小单元的编号,也就是地址
平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量
在管理内存时,最小的内存单元是一个字节,而为了方便找到某一个特点的内存单元,我们便对所有的内存单元进行编号,指针就是这个内存单元的编号,说白了,指针就是地址,地址就是编号
指针是内存中一个最小单元的编号,也就是地址
平时口语中说的指针,通常指的是指针变量,是用来存放内存地址的变量
在管理内存时,最小的内存单元是一个字节,而为了方便找到某一个特点的内存单元,我们便对所有的内存单元进行编号,指针就是这个内存单元的编号,说白了,指针就是地址,地址就是编号
&a,取的是第一个编号,由于地址是连续的,因此通过第一个地址,可以找到后面的地址
指针变量是用来存放地址的变量
对于32位的机器,假设有32根地址线,那么假设每根地址线在寻址的时候产生高电平(高电压)和低电 平(低电压)就是(1或者0)
这里就有2的32次方个地址。 每个地址标识一个字节,那我们就可以给 (2^32Byte == 2^32/1024KB == 2^32/1024/1024MB==2^32/1024/1024/1024GB == 4GB) 4G的空闲进行编址。 同样的方法,那64位机器,如果给64根地址线,那能编址多大空间,自己计算。
在32位的机器上,地址是32个0或者1组成二进制序列,那地址就得用4个字节的空间来存储,所以
一个指针变量的大小就应该是4个字节。 那如果在64位机器上,如果有64个地址线,那一个指针变量的大小是8个字节,才能存放一个地址。
总结
指针变量是用来存放地址的,地址是唯一标示一块地址空间的。
指针的大小在32位平台是4个字节,在64位平台是8个字节
VS编译器X86是32位环境,X64是64位环境
指针和指针类型
指针类型的意义
指针变量是用来存放地址的,地址是唯一标示一块地址空间的。
指针的大小在32位平台是4个字节,在64位平台是8个字节 VS编译器X86是32位环境,X64是64位环境指针类型的意义
把a的值通过pa修改,pa位int *类型
而把a的地址用char *类型指针获取时会报警告
强制类型转换后,警告消失
a此时在内存中这样存储,当解引用后
这里只将44变为了00,而其它位没变
总结
指针类型的不同,决定了在解引用的时候可以访问内存字节的个数
以上面位例:int *为4个字节,char *为1个字节,在解引用char *类型的只能访问int *类型一个字节
注意:指针所占内存的大小(只跟操作环境有关)跟指针可访问的字节数没任何关系
指针类型的不同,决定了在解引用的时候可以访问内存字节的个数
以上面位例:int *为4个字节,char *为1个字节,在解引用char *类型的只能访问int *类型一个字节
注意:指针所占内存的大小(只跟操作环境有关)跟指针可访问的字节数没任何关系
给它们分别加1
我们发现int *类型的指针加1,加了4个字节,而char *类型的指针加1,加了1个字节
总结2
指针类型决定了指针在加1和-1时候跳过几个字节,决定了指针一步能走多长
指针类型决定了指针在加1和-1时候跳过几个字节,决定了指针一步能走多长
这里的int *和float *是否可以通用,答案是不行,因为int *里面放的数据是int类型,而float *里面放的数据是float类型,俩种形式不一样,精度不一样
给*pi赋值为100,16进制下显示0x64
给*pf放100.0,我们此时可以看到内存中数字发生了变化 ,这是因为float *放数据的时候是按浮点型往里放,int *以整形往里面放
野指针
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
野指针就是指针指向的位置是不可知的(随机的、不正确的、没有明确限制的)
#includeint main() { int arr[10] = {0}; int *p = arr; int i = 0; for(i=0; i<=11; i++) { //当指针指向的范围超出数组arr的范围时,p就是野指针 *(p++) = i; } return 0; }
这里a在出函数时,会进行销毁,但是能通过指针访问到a,但由于返回时候返回的是地址,所以p里面保存着这个地址,但由于这块空间出函数的时候被销毁了,p对这块空间不能使用,只是记住了这块地址而已
一般情况下,如何避免野指针
当不知道指针赋什么值得时候,我们赋NULL即可, 但不能给这个空指针赋值
这里为什么会出现10,这是因为 ,虽然这块空间被销毁,但是它还未被别人使用,也就是没有被覆盖,里面放的还是10,只不过这块空间不属于a而已
此时,随便打印个东西数值就变了 ,这是因为hehe覆盖了这个a刚才使用得空间,也就是把里面得值覆盖了
指针运算
指针+-整数
#define N_VALUES 5
float values[N_VALUES];
float *vp;
//指针+-整数;指针的关系运算
for (vp = &values[0]; vp < &values[N_VALUES];)
{
*vp++ = 0; }
*vp=0然后vp++,指向下一个数据,这是给整个数组赋值为0,其实指针也可以访问到数组之后的空间,虽然这块空间不属于数组,但是可以访问
指针-指针
#define N_VALUES 5
float values[N_VALUES];
float *vp;
//指针+-整数;指针的关系运算
for (vp = &values[0]; vp < &values[N_VALUES];)
{
*vp++ = 0; }
*vp=0然后vp++,指向下一个数据,这是给整个数组赋值为0,其实指针也可以访问到数组之后的空间,虽然这块空间不属于数组,但是可以访问
指针-指针
|指针-指针|=中间元素个数,若用arr[0]-arr[9]=-9
注意;不是所有的指针都能相减,指向同一块空间的指针相减才有意义
用指针实现strlen函数
指针的关系运算
for(vp = &values[N_VALUES]; vp > &values[0];)
{
*--vp = 0; }
for(vp = &values[N_VALUES-1]; vp >= &values[0];vp--) {
*vp = 0; }
实际在绝大部分的编译器上是可以顺利完成任务的,然而我们还是应该避免这样写,因为标准并不保证 它可行
允许指向数组元素的指针与指向数组最后一个元素后面的那个内存位置的指针比较,但是不允许与 指向第一个元素之前的那个内存位置的指针进行比较。允许P1与P2比,不允许P1与P3比
指针和数组
#include
int main()
{
int arr[] = {1,2,3,4,5,6,7,8,9,0};
int *p = arr; //指针存放数组首元素的地址
int sz = sizeof(arr)/sizeof(arr[0]);
for(i=0; i p+%d = %pn", i, &arr[i], i, p+i);
}
return 0; }
二级指针
p是一个一级指针,z是一个二级指针,p的类型是int *,z的类型是int * *,
p里面放的是a的地址,*z里面放的是p的地址
*p里面放的是a的值,**z里面放的的*p的值
上面可以看到z的地址和其它的都不一样,这是因为z需要开辟一个空间,而这个空间是用来存它所指向的地址的
我们可以看到&p和p所指向的地址不一样,这是因为p要开辟一个空间来存它所指向的地址即&a
p:int *类型,p中存的是a的地址
&p:类型是int * *这个是开辟了一块空间,这块空间用来存指针p的,即a的地址,因为指针p指向a。
z:类型int** ,z中存的是p的地址,因为它是二级指针
&z:类型是int ***,这个是开辟了一个空间,这块空间用来存二级指针z的,即p的地址,因为二级指针z指向p
*z:是int*类型,里面存的是a的地址,和p一样
关系图
int * p
*告诉我们p是指针,int告诉我们p所指向的a是int类型
int * *z
最右边的*说明z是指针,前面的int和*合起来说明z所指向的对象类型是int
指针数组
指针数组是指针还是数组?是数组
指针数组是指针还是数组?是数组
#includeint main() { int a = 10; int b = 20; int c = 30; int arr[10]; int* pa = &a; int* pb = &b; int* pc = &c; int* parr[10] = { &a,&b,&c }; return 0; }
也可这样表示,因为&a也是int*类型,从左到右地址由低到高
也可将&符号去掉,为什么能将&去掉?这是因为arr1这些都是数组名,数组名是首元素地址,指针变量指向的是地址,所以能去掉
练习题
关于指针的概念,错误的是:( )
A.指针是变量,用来存放地址
B.指针变量中存的有效地址可以唯一指向内存中的一块区域
C.野指针也可以正常使用
D.局部指针变量不初始化就是野指针
A:正确,指针变量中存储的是一个地址,指向同类型的一块内存空间
B:正确,地址是唯一的,一个指针变量中只能存储一个地址,因此可以唯一指向内存中的一块区域
C:野指针指向的空间时非法的,或者说该指针指向的空间已经不存在了,因此野指针不能使用
D:局部指针变量没有初始化时里面就是随机值,因此指向那个位置不一定,故将其看成是野指针
以下系统中,int类型占几个字节,指针占几个字节,操作系统可以使用的最大内存空间是多大:( )
A.32位下:4,4,2^32 64位下:8,8,2^64
B.32位下:4,4,不限制 64位下:4,8,不限制
C.32位下:4,4,2^32 64位下:4,8,2^64
D.32位下:4,4,2^32 64位下:4,4,2^64
下面代码的结果是:( )
#includeint main() { int arr[] = {1,2,3,4,5}; short *p = (short*)arr; int i = 0; for(i=0; i<4; i++) { *(p+i) = 0; } for(i=0; i<5; i++) { printf("%d ", arr[i]); } return 0; } 这里的00 00 00 01 为四个字节因为是16进制,每俩个数字表示一个字节,如00表示一个字节
arr本来是int*类型,现在强制转换为short*类型
short*类型一次访问四个字节
下列程序段的输出结果为( )
unsigned long pulArray[] = {6,7,8,9,10}; unsigned long *pulPtr; pulPtr = pulArray; *(pulPtr + 3) += 3; printf(“%d,%dn”,*pulPtr, *(pulPtr + 3));.6,12
下面关于指针运算说法正确的是:( )
A.整形指针+1,向后偏移一个字节
B.指针-指针得到是指针和指针之间的字节个数
C.整形指针解引用操作访问4个字节
D.指针不能比较大小
指针能比较大小,只不过是在比较门牌号大小而已
下面代码输出的结果是:( )
#includeint main() { int a = 0x11223344; char *pc = (char*)&a; *pc = 0; printf("%xn", a); return 0; } 数据在内存中倒着存放,44332211会变为11223300,char*类型一次访问一个字节
这里会打印11223300
写一个函数,可以逆序一个字符串的内容。
#includeint main() { char arr[100] = { 0 }; gets(arr); int left = 0; int right = strlen(arr) - 1; char tmp; while (left < right) { tmp = arr[left]; arr[left] = arr[right]; arr[right] = tmp; left++; right--; } printf("%s", arr); return 0; }
求Sn=a+aa+aaa+aaaa+aaaaa的前5项之和,其中a是一个数字,
例如:2+22+222+2222+22222
#includeint main() { int a = 0; scanf("%d", &a); int i = 0; int j = 0; int t = 1; int sum = 0; for (i = 1; i <= 5; i++) { t = 1; for (j =1;j<=i;j++) { sum = sum + a *t; t *= 10; } } printf("%d", sum); return 0; }
#includeint main() { int a = 0; int n = 0; scanf("%d %d", &a, &n);//2 5 int i = 0; int sum = 0; int k = 0; for (i = 0; i < n; i++) { k = k * 10 + a; sum += k; } printf("%dn", sum); return 0; }
求出0~100000之间的所有“水仙花数”并输出。
“水仙花数”是指一个n位数,其各位数字的n次方之和确好等于该数本身,如:153=1^3+5^3+3^3,则153是一个“水仙花数”。
#include#include int fun(int a) { int i = 0; int sum = 0; if (a <10) return 1; else { while (a != 0) { a = a / 10; sum++; } return sum; } } void Print(int i, int sum) { int t = 0; int a = i; int z = 0; while (i) { if (i != 0) { t = i % 10; z = (int)pow(t, sum) + z; i = i / 10; } else if (i == 0) ; } if (a == z) printf("%d ",a); } int main() { int i = 0; int sum = 0; for (i = 0; i <= 10000; i++) { sum = fun(i); Print(i, sum); } return 0; }
打印菱形
#define _CRT_SECURE_NO_WARNINGS #includeint main() { int a; scanf("%d", &a); int i = 0; int j = 0; for (i = 0; i < a; i++) { for (j = 0; j <= a - i - 1; j++) { printf(" "); } for (j = 0; j <= i; j++) { printf("* "); } printf("n"); } for (i = 0; i < a; i++) { for (j = 0; j <= i+1; j++) { printf(" "); } for (j = 1; j <= a - i - 1; j++) { printf("* "); } printf("n"); } return 0; }



