指针
在计算机中,数据(变量)被储存在内存(地址)里
在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 = ⁣
*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 = ⁣
如此一来,就有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