指针详解练习

数组名是首元素地址,但是有2个例外,sizeof(数组名单独放在内部)和&数组名,它们两个表示整个数组,此外都是数组首元素。

123456789101112131415
#include <stdio.h>int main() {    int a[] = {1,2,3,4};    printf("%d\n",sizeof(a));//sizeof(数组名) - 计算的是数组总大小 - 单位是字节 - 16    printf("%d\n",sizeof(a+0));//打印的结果是4或8,这里是个指针,不符合sizeof(数组名)这种情况,a+0还是首元素地址    printf("%d\n",sizeof(*a));//打印的结果是4,首元素地址解引用,求的就是首元素的大小,int型的大小是4    printf("%d\n",sizeof(a+1));//打印的结果是4或8,结果和a+0是一样的,只不过不是第一个元素地址,而是第二个元素的地址    printf("%d\n",sizeof(a[1]));//打印的结果是4,计算的是第二个元素的大小    printf("%d\n",sizeof(&a));//打印的结果是4或8,&a取出的是数组的地址,数组地址的大小就是4或8    printf("%d\n",sizeof(*&a));//打印的结果是16,取地址取出来又解引用,按照sizeof(数组名)计算    printf("%d\n",sizeof(&a+1));//打印的结果是4或8,数组的地址+1,跳过一个数组,计算的还是一个地址的大小,4或8字节    printf("%d\n",sizeof(&a[0]));//4或8,第一个元素的地址的大小    printf("%d\n",sizeof(&a[0]+1));//4或8,第二个元素的地址    return 0;}

' '单引号括起来的字符型数组,最后没有\0,例如下面的数组,只有a,b,c,d,e,f六个元素。

123456789101112
#include <stdio.h>int main() {    char arr[] = {'a','b','c','d','e','f'};    printf("%d\n",sizeof(arr));//sizeof(数组名) - 计算的是数组总大小 - 单位是字节 - 大小是6字节    printf("%d\n",sizeof(arr + 0));//打印的结果是4或8,这里是首元素地址,地址的大小都是4或8字节    printf("%d\n",sizeof(*arr));//打印的结果是1,对arr解引用,就是arr首元素大小    printf("%d\n",sizeof(arr[1]));//打印的结果是1    printf("%d\n",sizeof(&arr));//打印的结果是4或8,&arr取出的是数组的地址,数组地址的大小就是4或8    printf("%d\n",sizeof(&arr+1));//打印的结果是4或8,数组的地址+1,跳过一个数组,计算的还是一个地址的大小,4或8字节    printf("%d\n",sizeof(&arr[0]+1));//第二个元素的地址,地址大小是4或8字节    return 0;}

' '单引号括起来的字符型数组,最后没有\0,例如下面的数组,只有a,b,c,d,e,f六个元素。

12345678910111213
#include <stdio.h>#include <string.h>int main() {    char arr[] = {'a','b','c','d','e','f'};    printf("%d\n",strlen(arr));//随机值,strlen计数直到遇见一个\0止    printf("%d\n",strlen(arr + 0));//随机值,arr+0和arr是一样的,都是计数直到遇见一个\0为止    printf("%d\n",strlen(*arr));//报错,*arr不是地址,strlen()括号内需要地址    printf("%d\n",strlen(arr[1]));//报错,arr[1]不是地址,strlen()括号内需要地址    printf("%d\n",strlen(&arr));//随机值,虽然传送地址过去,但arr数组最后一个不是\0    printf("%d\n",strlen(&arr+1));//随机值,跳过arr数组,知道找到下一个\0    printf("%d\n",strlen(&arr[0]+1));//随机值,从第二个开始找\0    return 0;}

" "双引号括起来的字符型数组,最后还有一个\0。例如下面的数组,有a,b,c,d,e,f,\0七个元素。

123456789101112
#include <stdio.h>int main() {    char arr[] = {"abcdef"};    printf("%d\n",sizeof(arr));//sizeof(数组名) - 计算的是数组所占空间大小 - 单位是字节 - 大小是7字节 - 最后有一个\0    printf("%d\n",sizeof(arr + 0));//打印的结果是4或8,这里是首元素地址,地址的大小都是4或8字节    printf("%d\n",sizeof(*arr));//打印的结果是1,对arr解引用,就是arr首元素大小    printf("%d\n",sizeof(arr[1]));//打印的结果是1,第二个元素的大小    printf("%d\n",sizeof(&arr));//打印的结果是4或8,&arr取出的是数组的地址,数组地址的大小就是4或8    printf("%d\n",sizeof(&arr+1));//打印的结果是4或8,数组的地址+1,跳过一个数组,计算的还是一个地址的大小,4或8字节    printf("%d\n",sizeof(&arr[0]+1));//第二个元素的地址,地址大小是4或8字节    return 0;}

12345678910111213
#include <stdio.h>#include <string.h>int main() {    char arr[] = {"abcdef"};    printf("%d\n",strlen(arr));//strlen(数组名) - strlen 计算到\0止,数组最后有一个\0,打印的结果是6    printf("%d\n",strlen(arr + 0));//首元素地址开始找\0,打印的结果是6,和上面的意思是一样的    printf("%d\n",strlen(*arr));//报错,*arr不是地址,strlen内部需要输入地址    printf("%d\n",strlen(arr[1]));//报错,arr[1]不是地址,strlen()括号内需要地址    printf("%d\n",strlen(&arr));//结果是6,strlen找到\0停止计数    printf("%d\n",strlen(&arr+1));//随机值,不知道下一个数组的长度。    printf("%d\n",strlen(&arr[0]+1));//结果是5,从第二个元素开始计数,6-1 = 5    return 0;}

p里面存放常量字符串中a的地址,常量字符串里包含a,b,c,d,e,f,\0。

12345678910111213
#include <stdio.h>#include <string.h>int main() {    char *p = "abcdef";    printf("%d\n",sizeof(p));//打印的结果是4或8,指针相当于地址,就是字符串中a的指针的大小。    printf("%d\n",sizeof(p + 1));//p+1就是字符串中第二个字符的地址,即b的地址,地址的大小就是4或8    printf("%d\n",sizeof(*p));//打印的结果是1,*p就是字符串第一个字符    printf("%d\n",sizeof(p[0]));//打印的结果是1,就是字符串中第一个字符。    printf("%d\n",sizeof(&p));//打印的结果是4或8,就是首元素的地址,地址都是4或8    printf("%d\n",sizeof(&p+1));//打印的结果是4或8,就是第二个元素的地址,地址都是4或8    printf("%d\n",sizeof(&p[0]+1));//先取到了a的地址,再加1,就是b的地址,就是4或8    return 0;}

12345678910111213
#include <stdio.h>#include <string.h>int main() {    char *p = "abcdef";    printf("%d\n",strlen(p));//打印的结果是6,p里面存的就是a的地址,即从a找到\0    printf("%d\n",strlen(p + 1));//p+1就是字符串中第二个字符的地址,即b的地址,打印的结果就是6-1=5    printf("%d\n",strlen(*p));//报错,*p解引用后不是地址了,strlen内需要地址    printf("%d\n",strlen(p[0]));//报错,p[0]不是地址,strlen内需要地址    printf("%d\n",strlen(&p));//随机值,&p是二级指针    printf("%d\n",strlen(&p+1));//随机值,&p是二级指针,二级指针+1是这个二级指针的下一个空间    printf("%d\n",strlen(&p[0]+1));//打印的结果是5,即从b开始数    return 0;}

二维数组的大小:

12345678910111213141516
#include <stdio.h>int main() {    int a[3][4] = {0};    printf("%d\n",sizeof(a));//3*4*4=48,sizeof内放数组名,计算数组总大小    printf("%d\n",sizeof(a[0][0]));//第一行第一个元素的大小,4字节    printf("%d\n",sizeof(a[0]));//计算的是第一行元素大小,四个整型元素,4*4=16    printf("%d\n",sizeof(a[0] + 1));//计算的是第一行第二个元素的地址,占4或8字节,a[0]是第一行数组名,此时是首元素的地址,就是第一行第一个元素的地址。a[0] + 1就是第一行第二个元素的地址    printf("%d\n",sizeof(*(a[0] + 1)));//4,第一行第二个元素(不是地址),整型占4字节    printf("%d\n",sizeof(a+1));//占4字节或8字节。a是二维数组的数组名,a是首元素地址,二维数组的首元素是第一行(把二维数组看成1维数组)。加1就是第二行的地址    printf("%d\n",sizeof(*(a+1)));//16字节,对第二行地址解引用,就等价于a[1]。就是第二行的大小。    printf("%d\n",sizeof(&a[0]+1));//第二行的地址,占4或8字节    printf("%d\n",sizeof(*(&a[0]+1)));//16字节,对第二行地址解引用。    printf("%d\n",sizeof(*a));//16字节,*a是第一行的地址的解引用,4*4    printf("%d\n",sizeof(a[3]));//16字节,虽然野指针,第四行元素大小(虽然没有)。    return 0;}

总结:

  • sizeof(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
  • &数组名,这里的数组名表示的是整个数组,取出的是整个数组的地址。
  • 除此之外,所有的数组名都表示首元素的地址。

1234567
#include <stdio.h>int main() {    int a[5] = {1,2,3,4,5};    int *ptr = (int *)(&a + 1);    printf("%d,%d\n",*(a + 1),*(ptr - 1));    return 0;}

打印的结果是2,5
&a + 1跳过整个数组,进入下一个数组,(int *)转换成整型指针。
(ptr - 1)整型指针-1,向前挪动4字节,再解引用,也就是5.


1234567891011121314151617181920
#include <stdio.h>struct Test{    int Num;    char *pcName;    short sDate;    char cha[2];    short sBa[4];}* p; //假设p的值为0x100000,求下面的值//已知结构体Test的类型的变量大小是20字节 int main(){    printf("%p\n",p +0x1);    printf("%p\n",(unsigned long)p + 0x1);    printf("%p\n",(unsigned int*)p + 0x1);    return 0;}

printf("%p\n",p +0x1); p+0x1就相当于是p+1,先看p的类型,是结构体指针类型,就是跳过一个结构体,也就是0x100000+20,结果是0x100014
printf("%p\n",(unsigned long)p + 0x1); 就是整型+1,先把0x100000转换成10进制,即1048576+1,再转换成16进制,也就是0x100001。
printf("%p\n",(unsigned int*)p + 0x1);,强转成无符号整型指针,+1跳过一个无符号整型的大小,即4字节,也就是0x100004。
指针+-整数,取决于指针类型。


12345678
#include <stdio.h>int main(){    int a[4] = {1,2,3,4};    int *ptr1 = (int *)(&a+1);    int *ptr2 = (int *)((int)a+1);    printf("%x%x",ptr1[-1],*ptr2);}

ptr1是一个指针,指向a后面的一个地址。也就是4
((int)a+1)将a的地址强制转换为整数,再加1。最后(int *)再转换为地址。即0x02000000


12345678
#include <stdio.h>int main(){    int a[3][2] = {(0,1),(2,3),(4,5)};    int *p;    p = a[0];    printf("%d",p[0]);}

nt a[3][2] = {(0,1),(2,3),(4,5)};数组内有逗号表达式,
a[0][0] = 1,a[0][1] = 3,a[1][0] =5,后面没有初始化,默认为0
p = a[0];a[0]是第一行数组名,即a[0][0]的地址存到p中
打印的结果是1。


12345678
#include <stdio.h>int main(){    int aa[2][5] = {1,2,3,4,5,6,7,8,9,10};    int *ptr1 = (int*)(&aa+1);    int *ptr2 = (int*)(*(aa+1));    printf("%d,%d\n",*(ptr1-1),*(ptr2-1));}

对于ptr1:
(&aa+1)相当于是跳过了这个二维数组,也就是10后面的那一个指针,是一个数组指针。
(int*) 把这个数组指针强转成了整型指针。
对于ptr2:
aa表示二维数组的第一行,+1就进入了第二行。
对它进行解引用,拿到了第二行的首元素的地址,即6
ptr2就是6的指针。
结果就是10,5


123456789
#include <stdio.h>int main(){    char *a[] = {"work","at","alibaba"};    char**pa = a;    pa++;    printf("%s\n",*pa);    return 0;}

char *p = "abcdef"这种写法是把这个常量字符串的首字母a的地址放到p指针里面去。

char *a[] = {"work","at","alibaba"}; 这句的意思就是把w、a、a(首字母)的地址放到a这个char型数组里。
char**pa = a;表示把数组a的首元素地址放到pa里。
pa++也就是第二个元素的首字母地址。
打印的结果是at


12345678910
#include <stdio.h>int main(){    unsigned long pulArray[] = {6,7,8,9,10};    unsigned long *pulPtr;    pulPtr = pulArray;    *(pulPtr + 3) += 3;       printf("%d,%d\n",*pulPtr,*(pulPtr + 3));    return 0;}

pulPtr = pulArray;就是把pulArray这个数组的首元素地址赋给pulPtr。
*(pulPtr + 3)就是先把pulPtr的地址+3,也就是从pulArray的首元素往后数三个元素,也就是9这个元素的地址。再解引用,也就是9。最后9+=,即12。也就是原先9这个元素的地址变成了12。


字符串逆序

写一个函数,可以逆序一个字符串的内容。

1234567891011121314151617181920212223242526
#include <stdio.h>void Reverse(char* arr,int sz){    int left = 0;    int right = sz-1;    char temp = 0;    for (left = 0;left<sz/2;left++)    {        temp = arr[left];        arr[left] = arr[right];        arr[right] = temp;        right--;    }} int main(){    char arr[] = {'a','b','c','d','e','f','g','h','i','j'};    int sz = sizeof(arr)/sizeof(arr[0]);    int i = 0;    Reverse(arr,sz);    for (i=0; i<sz; i++) {        printf("%c ",arr[i]);    }    printf("\n");}

计算a+aa+aaa+aaaa+aaaaa的前5项之和,a是数字

S_n=2+22+222+2222+22222,中n=5,x=2

123456789101112131415161718
#include <stdio.h>int main(){    int x = 0;    int n = 0;    int sum = 0;    int i = 0;    int ret = 0;    printf("Sn = 2 + 22+ 222 + 2222 + 22222\n");    printf("上述x = 2,n = 5\n");    printf("请输入x和n:->");    scanf("%d%d",&x,&n);    for (i=0; i<n; i++) {        ret = ret*10 + x;        sum = sum+ ret;    }    printf("%d\n",sum);}

求1-100000之间的自幂数

一个数字有n位数,每一位的n次方之和等于它本身。
例如153 = 1^3 + 5^3 + 3^3.

1234567891011121314151617181920212223242526
#include <stdio.h>#include <math.h>int main(){    int i = 0;    for (i=0; i <= 100000; i++) {        //先知道是几位数        int n = 1;        int tmp = i;        int sum = 0;        while (tmp /= 10)        {            n++;        }        //计算每一位的n次方之和 sum        tmp = i;        while (tmp) {            sum = sum + pow(tmp % 10,n);            tmp /= 10;        }        //比较        if (i == sum) {            printf("%d ",i);        }    }}

求一个数字的次方:pow(数字,次方数)


打印菱形

12345678910111213
      *     ***    *****   *******  ********* ************************ ***********  *********   *******    *****     ***      *
123456789101112131415161718192021222324252627282930313233
#include <stdio.h>int main(){    int line = 0;    scanf("%d",&line);//7    //打印上部分    int i = 0;    for (i = 0; i<line; i++) {        //打印空格        int j = 0;        for (j = 0; j<line-i-1; j++) {            printf(" ");        }        //打印星号        for (j=0; j<2*i+1; j++) {            printf("*");        }        printf("\n");    }    //打印下部分    for (i=0; i<line-1; i++) {        //打印空格        int j = 0;        for (j = 0; j<=i; j++) {            printf(" ");        }        //打印星号        for (j=0; j<2*(line-1-i)-1; j++) {            printf("*");        }        printf("\n");    }}
🔍 ×