C语言指针知识记录
前言
在学习过程中老是遇到很多坑,或者单纯是自己没掌握好的东西。不记录下来过一段时间又忘了。因此写下来作为记录,希望能帮到后来的朋友爬坑,同时也方便自己以后查阅。如有不正确的地方欢迎指出。
地址
程序的数据是存储在电脑中的虚拟存储器中的。这个虚拟存储器里面有许多单元,每个单元都有自己的标号,也就是这个单元的地址。指针的本质就是某个单元的地址。
比如,int n = 5 ; 声明了一个叫n的变量,并把它的值赋为5。在计算机内部这个值实际是虚拟存储器的某个单元中的,这个单元的地址可以通过取地址符号,&n获得。
其实可以把虚拟内存看成一个很大的数组,指针就是数组的下标。&n就是取存了n的单元的下标。
#include int main(void)
{
char ch = 'a';
int n = 5;
printf("ch 的地址:%p\n",&ch); //ch 的地址:0061FECF
printf("n 的地址:%p\n",&n); //n 的地址:0061FEC8
return 0;
}
指针
指针的本质是某个单元的地址,因此赋值时候,就应该把一个地址赋给它,比如int *p = &n;表示的是申请了一个int类型的指针,这个指针的内容为n的地址。因此,这个指针也就指向了n的。
p是一个指针,*p为指针所指向的内容。
#include int main(void)
{
int n = 5;
int *p = &n;
printf("p 的值:%p\n",p); // p的值:0061FEC8
printf("*p 的值:%d\n",*p); // *p的值:5
return 0;
}
关于指针的最基础的知识就是这些了,更详细更深入的知识可以参考博客: C语言指针详解
下面写一写我在学习中遇到的坑。
1. 字符串常量
先来看一段会报错的代码。原因是 “helloworld” 被存在一个只读的区域中,所以其实是常量,不能被修改。我这里是程序异常终止Process finished with exit code -1073741819 (0xC0000005)。
#include int main(void)
{
int i ;
char *s1 = "helloworld";
s1[0] ='H';
printf("%c",s1[0]);
return 0;
}
解释:
在C语言中 “helloworld” 叫做字符串字面量,也叫字符串常量。“helloworld” 会被编译器编程一个字符数组放在某处,这个数组的长度是 “helloworld” 的长度加1。(字符串末尾有\0)
char *s = “helloworld"含义为:s是一个指针,指向一个字符串常量,这个字符串常量是在编译的时候赋值的,被储存在一个叫"代码段”,这个地方是只读的。
因此char *s = “helloworld” 实际上是 const char *s = “helloworld”
接下来再看一段代码:
#include int main(void)
{
int i ;
char *s1 = "helloWorld";
char *s2 = "helloWorld";
printf("&i = %p\n",&i);
printf("s1 = %p\n",s1);
printf("s2 = %p\n",s2);
return 0;
}
通过以上代码可知,s1,s2因为指向同一个区域,因此相同。并且,s1,s2都比较小,明显和i的地址不在同一块区域。
关于不同变量存储位置详细可以参考博客C/C++程序内存的各种变量存储区域和各个区域详解
2. 用指针代替二位数组
前段时间写了个代码实现矩阵的基本运算。矩阵的数据结构如下。
typedef struct {
double **mat;
int row;
int col;
}Matrix;
用 **mat 代替 mat[MAXN][MAXN] 就能利用malloc开出合适的空间,避免空间浪费。
但由于学艺不精,把自己搞迷糊了,调试了很久才弄对。下面是矩阵初始化函数的代码。
void InitialMatrix(Matrix *T, int row, int col) {
T->row = row;
T->col = col;
T->mat = (double**)malloc(sizeof(double*)*row);
for (int i=0;imat[i] = (double*) malloc(col*sizeof(double ));
}
存储结构即为先申请row个指针,让他们分别指向col个double的空间。
3. 指针代替结构体数组
做大作业学生成绩管理系统,自己做肯定会用链表来存学生信息,但根据题目要求来看,得做成结构体数组的形式,当然还是需要用到指针,不能暴力的就来一个student stu[MAXN] 了。虽说现在的计算机也不差那点空间了,但是该学的知识还是要学好,奈何本人实在太菜,又被卡了很久。
下面是学生信息数据结构。
typedef struct
{
int id;
char name[50];
float chinese;cloat english; float math;float sum;
}student;
下面是题目的主程序。stu是一个指针,学生信息是存在一个指针里的,但给出的学生信息数据结构并不是链表。 ReadStuInfoFromFile ( ) 传入的又是stu这个指针的地址。没办法了,只能在ReadStuInfoFromFile ( ) 做一个结构体数组,然后将stu修改为这个结构体数组的地址。
int main() {
int n, rank, id,i,m;
student *stu =NULL;
n = ReadStuInfoFromFile(filepath,&stu);
PrintStudents(stu, n);
下面是ReadStuInfoFromFile () 的实现。
int ReadStuInfoFromFile(char *filepath,student ** pstu) {
FILE *fp = NULL;
fp = fopen(filepath,"r");
if (fp==NULL) return 0;
int n;
fscanf(fp,"%d",&n);
/* 只关注下面数据结构实现的部分*/
student *stu;
stu = (student*) malloc(n*sizeof(student));
for (int i = 0; i < n ; ++i) {
fscanf(fp,"%d %s %f %f %f",&stu[i].id,stu[i].name,&stu[i].chinese,&stu[i].math,&stu[i].english);
stu[i].sum = stu[i].chinese+stu[i].english+stu[i].math;
}
*pstu = stu;
fclose(fp);
return n;
}
在ReadStuInfoFromFile ( ) 里,stu是一个指针,即stu得到应该是一个地址。
malloc申请了一个大小为 n* sizeof(student) 的空间,是一片可分成 n 份的连续的空间,其实也就是一个产生了一个student类型的数组。这个数组的地址即为这这片空间的首地址。因此stu最后得到的其实就是这片空间的的首地址。
前面说过要将main() 里的stu改为结构体数组的地址。
pstu 是 main() 函数里的stu的地址,因此修改main() 里的stu ,需要通过 *pstu来修改。
因此 *p = stu;
暂时就先写到这里了,以后遇到关于指针的问题再来补充。