0%

指针详解练习

文章时效性提示

本文发布于 511 天前,部分信息可能已经改变,请注意甄别。

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
#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六个元素。

1
2
3
4
5
6
7
8
9
10
11
12
#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六个元素。

1
2
3
4
5
6
7
8
9
10
11
12
13
#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七个元素。

1
2
3
4
5
6
7
8
9
10
11
12
#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;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
#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。

1
2
3
4
5
6
7
8
9
10
11
12
13
#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;
}

1
2
3
4
5
6
7
8
9
10
11
12
13
#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;
}

二维数组的大小:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#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(数组名),这里的数组名表示整个数组,计算的是整个数组的大小。
  • &数组名,这里的数组名表示的是整个数组,取出的是整个数组的地址。
  • 除此之外,所有的数组名都表示首元素的地址。

1
2
3
4
5
6
7
#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.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#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。
指针+-整数,取决于指针类型。


1
2
3
4
5
6
7
8
#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


1
2
3
4
5
6
7
8
#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。


1
2
3
4
5
6
7
8
#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


1
2
3
4
5
6
7
8
9
#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


1
2
3
4
5
6
7
8
9
10
#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。


字符串逆序

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
#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.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#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(数字,次方数)


打印菱形

1
2
3
4
5
6
7
8
9
10
11
12
13
      *
     ***
    *****
   *******
  *********
 ***********
*************
 ***********
  *********
   *******
    *****
     ***
      *
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
#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");
    }
}