C码字练习.幕间.有关理解指针的补充 - 北方连萌

C码字练习.幕间.有关理解指针的补充

指针

在计算机中,数据(变量)被储存在内存(地址)里

在Fortran等语言中,计算机自动分配地址,而在C中,用户则可以对地址进行处理以期实现更精细的操作

进行这一处理的,就是所谓指针了

在C中,取地址&与解引用*互为逆运算

也就是说,a是变量,p是a的地址时,有以下关系

&a //取a的地址,若输出则是p的值
*p //对p解引用,若输出则是a的值

亦即

&a //指向变量a的指针,是p
*p //指针p指向的变量,是a

a的值是变量的值,p的值可当成表示地址的一串代号,当然p自己也有地址代号,也可以取地址

创建指针变量后可以直接解引用代值吗

答案:不可以,会出错

错误示范

#include<stdio.h>
int main(void)
{
   int * a, * b, * c; //三个指向整数的指针变量a, b, c
   *a = 2;
   *b = 3;
   *c = *a + *b;
   printf("%d", *c);

   return 0;
}

在本例中运行这段,没有输出任何东西

正确示范

#include<stdio.h>
int main(void)
{
   int * a, * b, * c; //三个指向整数的指针变量a, b, c,储存的地址是随机的
   int ia, ib, ic; //三个整型变量ia, ib, ic,创建时即被分配了不同的地址
   a = &ia;
   b = &ib;
   c = &ic;
   *a = 2;
   *b = 3;
   *c = *a + *b;
   printf("%d", *c);

   return 0;
}

运行后正确地输出了结果

5

原因

指针未初始化

int * a, * b, * c;

这一段相当于只在内存中分配了3个位置以储存3个指针,值是随机的

稍作回顾,指针解引用是根据在其上储存的地址代号,找到地址上储存的变量的值

那么当把数字直接放进解引用的指针的时候,相当于把这个数字扔进了随机的地址里

实际上在本例中

#include<stdio.h>
int main(void)
{
   int * a, * b, *c;
   printf("%d\n", a);
   printf("%d\n", b);
   printf("%d\n", c);

   return 0;
}

的结果是

16
11932592
16

a和c甚至指向了同一地址

所以解决办法最简单的就是用已分配的地址初始化指针,一个变量对应一个地址,使赋值不混乱

int * a, * b, * c;
int ia, ib, ic;
a = &ia;
b = &ib;
c = &ic;

如此一来,就有ia ib ic三个位置储存三个数字了

数组和指针

对指针做加法

数组名是数组首元素的地址,详见下例

#include<stdio.h>
int main(void)
{
   char a[2];
   short b[2];
   int c[2];
   long d[2];
   float e[2];
   double f[2];

   printf("a = %d\n", a);
   printf("&a[0] = %d\n", &a[0]);
   printf("&a[1] = %d\n", &a[1]);
   printf("b = %d\n", b);
   printf("&b[0] = %d\n", &b[0]);
   printf("&b[1] = %d\n", &b[1]);
   printf("c = %d\n", c);
   printf("&c[0] = %d\n", &c[0]);
   printf("&c[1] = %d\n", &c[1]);
   printf("d = %d\n", d);
   printf("&d[0] = %d\n", &d[0]);
   printf("&d[1] = %d\n", &d[1]);
   printf("e = %d\n", e);
   printf("&e[0] = %d\n", &e[0]);
   printf("&e[1] = %d\n", &e[1]);
   printf("f = %d\n", f);
   printf("&f[0] = %d\n", &f[0]);
   printf("&f[1] = %d\n", &f[1]);
   
   return 0;
}
a = 6422046
&a[0] = 6422046
&a[1] = 6422047
b = 6422042
&b[0] = 6422042
&b[1] = 6422044
c = 6422032
&c[0] = 6422032
&c[1] = 6422036
d = 6422024
&d[0] = 6422024
&d[1] = 6422028
e = 6422016
&e[0] = 6422016
&e[1] = 6422020
f = 6422000
&f[0] = 6422000
&f[1] = 6422008

另外可以看出各型单位长度:

char 1字节

short 2字节

int 4字节

long 4字节

float 4字节

double 8字节

再以c[2]为例,尝试对指针加法

#include<stdio.h>
int main(void)
{
   int c[2];

   printf("c = %d\n", c);
   printf("&c[0] = %d\n", &c[0]);
   printf("&c[1] = %d\n", &c[1]);
   printf("c + 1 = %d\n", c + 1);

   return 0;
}
c = 6422040
&c[0] = 6422040
&c[1] = 6422044
c + 1 = 6422044

故,对指针加1就是加上一个相应型的单位长度

c[0] == *c
c[1] == *(c + 1)

二维数组

例如

int a[2][3] = {{1,3,3},{2,4,4}}

当然用矩阵(行列)的方式可以解读为

$$ \begin{pmatrix}1&3&3\\ 2&4&4\end{pmatrix} $$

但从数组的方式,以下解读

a是一个有2个元素的数组 (a[2]),其中每个元素都是内含3个整数的数组 (int [3])

以下例见得

#include<stdio.h>
int main(void)
{
   int a[2][3] = {{1,3,3},{2,4,4}};
   int i;

   for (i = 0; i < 2; i++)
   {
      printf("a[%d] = %d", i, a + i);
      putchar('\n');
   }
   
   return 0;
}
a[0] = 6422016
a[1] = 6422028

两个元素之间相差12字节,正好是三个整型的单位长度

此外,再有

#include<stdio.h>
int main(void)
{
   int a[2][3] = {{1,3,3},{2,4,4}};

   printf("&a[0][0] = %d\n", &a[0][0]); //首行首元素的地址即a[0]
   printf("&a[1][0] = %d\n", &a[1][0]); //次行首元素的地址即a[1]
   printf("*a[0] = %d\n", *a[0]); //解引用后是首行首元素
   printf("*(a[0] + 3) = %d\n", *(a[0] + 3)); //解引用后是次行首元素

   return 0;
}
&a[0][0] = 6422016
&a[1][0] = 6422028
*a[0] = 1
*(a[0] + 3) = 2

如此,对「每行长度不一」的「行列」可以有更简便的理解,例如

#include<stdio.h>
#include<stdlib.h> //malloc
#include<string.h> //strcpy, strlen

int main(void)
{
   int i, j; //计数器
   char * pstr[5]; //可容纳5个指针的数组
   char str[100]; //可容纳100个字符的数组

   for(i = 0; i < 5; i++)
   {
      scanf("%s", str); //读取字符串,str前不用加&
      pstr[i] = (char *)malloc(sizeof(char) * (strlen(str)+1)); /*malloc函数*/
      strcpy(pstr[i], str); //复制,把后者复制到前者
      for (j = 0; j < strlen(str); j++)
      {
         printf("%c", pstr[i][j]); //输出
      }
      printf("%s", "_copy");
      putchar('\n');
   }

   return 0;
}
tttttt
tttttt_copy
uuufcgvhbjn
uuufcgvhbjn_copy
rdrcghbn876
rdrcghbn876_copy
gtf45drt
gtf45drt_copy
inu76ybb
inu76ybb_copy

分配内存空间函数malloc

调用形式

(type *)malloc(size)

功能

在内存的动态存储区中分配一块长度为size字节的连续区域,函数的返回值为该区域的首地址

type表示把该区域用于何种数据类型

(type *)表示把返回值强制转换为该类型指针

size是一个无符号数

上例中

pstr[i] = (char *)malloc(sizeof(char) * (strlen(str)+1))

表示分配sizeof(char) * (strlen(str)+1)字节的内存空间,并强制转换为字符数组类型,函数的返回值为指向该字符数组的指针,把该指针赋予指针变量pstr[i]

其中

sizeof(char) //字符型单位长度
(strlen(str)+1) //字符数+1,+1的目的为数组末尾添加\0

添加新评论

电子邮件地址不会被公开,评论内容可能需要管理员审核后显示。