扫二维码与项目经理沟通
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流
有人称指针是C语言的灵魂,有人又称指针说指针是C语言最难的部分,那么,指针对C语言来说到底意味着什么?指针到底是不是C语言和C++经久不衰的原因之一呢?
成都创新互联公司是一家专业提供富民企业网站建设,专注与网站建设、成都网站建设、H5技术、小程序制作等业务。10年已为富民众多企业、政府机构等服务。创新互联专业网站设计公司优惠进行中。指针的问题不是三言两语就能表达清楚的,而在《C和指针》第六章中也仅仅是提到了指针有关的部分内容,指针的精彩与巧妙之处,还需要在后续的章节和具体的项目代码中细细体会。先来看看本章内容的基本框架(思维导图)。
思维导图
从思维导图里可以看出,关于指针的内容还是比较多的,我们的重点需要放在指针表达式的理解上。
内存和地址是一个很深刻的话题,为了方便地访问计算机的内存数据,我们会对其进行编号,就像现实中我们邮寄地址的邮政编码一样。书中提到,我们只对两件事情感兴趣。
在现实中我们需要保存各种各样的数值,然而对于计算机的内存来说,只有0
和1
,所以某内存中的到底表示什么值,则需要看被解释为什么值。
int a = 112, b = -1;
float c = 3.14;
int *d = &a;
float *e = &c;
然而在内存中,确有着如下的数值。
所以书中特别强调:不能简单地通过检查一个值的位来判断它的类型。
从6.2
小节的内容可以看出,d和e中存储的值恰好差8,也就是两个int型变量的距离(因为一个int型变量一般占4个字节),那么变量d
和e
的值到底是多少呢?究竟是其本身的值呢?还是其指向内存地址内所保存的值呢?
按照规定,变量的值就是分配给变量的内存位置所存储的数值,即使是指针变量也不例外。从这个规定来看,变量d
和e
的值应该分别是100
和108
。
通过一个指针访问它所指向的地址的过程称为间接访问或解引用指针。这个用于执行间接访问的操作符是单目操作符*
。下面还是通过以上的一个变量看看间接访问操作符如何访问所需的数值。
int main()
{int a = 112, b = -1;
float c = 3.14;
int *d = &a;
float *e = &c;
printf("a = %d\n", *d);
printf("c = %f\n", *e);
system("pause");
}
我们来看看打印输出:
确实获取到了我们需要的数值。
书中有这样的例子:
int *a;
*a = 12;
显然这样的表达是有问题的,从程序中来看,a指向某地址的值为12,但是究竟12位于哪个地址是未知的,所以可以写成下面的形式。
int num = 12;
int *a = #
printf("结果为:%d\n", *a);
system("pause");
return 0;
虽然我们并不能直接知道num究竟保存在什么地方,但是可以通过指针a间接访问num中存储的值。
6.6 NULL指针NULL
指针相当于是指针初始化的一种方式,表示当前指针并未指向任何内存地址,以为有时候编程的时候会遇到,需要定义一个指针,但是暂时并未想好要指向的内存地址,这个时候NULL
指针就有了用武之地。举个例子。
剑指 Offer 50. 第一个只出现一次的字符
在字符串 s 中找出第一个只出现一次的字符。如果没有,返回一个单空格。
s只包含小写字母。
typedef struct hash
{int key;
int val;
UT_hash_handle hh;
}hash;
char firstUniqChar(char* s) {int len = strlen(s);
hash *f = NULL;
for(int i = 0; i< len; i++)
{int ele = s[i];
hash *temp = NULL;
HASH_FIND_INT(f,&ele,temp);
if(temp == NULL)
{temp = malloc(sizeof(hash));
temp->key = ele;
temp->val = 1;
HASH_ADD_INT(f,key,temp);
}
else
temp->val++;
}
for(int i = 0; i< len; i++)
{int ele = s[i];
hash *temp = NULL;
HASH_FIND_INT(f,&ele,temp);
if(temp != NULL && temp->val == 1)
{return temp->key;
}
}
return ' ';
}
创建了一个哈希表。用来存放字符数组s中出现的各个字母及其出现的频率,在刚刚定义的时候,还没有开始统计,所以不是很好确定到底要指向何处,因此出现了下面的表达式。
hash *f = NULL;
6.7 指针、间接访问和左值当指针变量作为左值的时候,会出现什么样的情况呢?我们一起来看看书上给的例子(有改动)。
int a = 4;
int *d = &a;
*d = 10 - *d;
printf("结果为:%d\n", *d);
system("pause");
return 0;
打印输出如下:
可以看出在语句*d = 10 - *d
中,该语句可以这样理解:用10减去指针d
所指向地址的值,并保存在原地址。
书中提到了这样的运算:*&
细品之下好像并没有什么作用,因为先进行了取地址运算,再间接访问。举个例子:
int a = 4;
*&a = 40;
printf("结果为:%d\n", a);
system("pause");
return 0;
打印输出:
可以看出,这与直接对变量a进行赋值并没有什么不同。
关于指针常量的定义(还有指针常量和常量指针的区别),我们在此前的章节中介绍过,具体见:
第3章 数据
书中介绍了这样一种情况,如果我们想在指定位置存储指定的值,应该怎么办呢?比方说在地址100的地方存储25,难道我们直接像下面这样:
*100 = 25;
其实这是错误的做法,因为间接访问操作符只能作用于指针,而100是具体的数值,所以就会出现问题,正确的做法应该是下面这样,使用强制类型转换。
*(int *)100 = 25;
虽然书上是这样介绍的,但实际在VS的集成开发环境中会报错,可能在其他环境中不会报错。因为我们直接编辑的内存地址有可能是系统不允许访问的地址,等等。这个表达式必须要知道,因为很多公司的面试题中会考到。
6.10 指针的指针听起来有点绕,但是这种操作(思想)在C语言的编程中非常常见。书上有个例子:
int a = 12;
int *b = &a;
int **c = &b;
printf("结果为:%d\n", **c);
system("pause");
return 0;
打印输出:
将a
的地址保存在了b
中,然后再将b
的地址保存在了c
中,这样的操作看着很无聊,但是在后面的章节中就会发现其中的奥妙。
这部分内容非常非常重要!!!因为全都是常见的指针表达式,这部分内容对理解指针有相当大的帮助。
特别提示:
所谓的作为左值,可理解为往该变量写入数值,
作为的作为右值,可理解为从该变量读出数值。
首先我们对ch
和cp
作了以下声明:
char ch = 'a';
char *cp = &cp;
总结了书上提到的所有情况,并作了相应的解释(好辛苦啊啊啊啊啊啊啊啊)。
其中的虚线框表示中间的计算结果,实线框表示得到的结果或要写入的地址空间。
书上给出了一个求字符串长度的函数,其实这个函数本身库里面也有。
size_t strlen(char *string)
{int length = 0;
while (*string++ != '\0')
length++;
return length;
}
int main()
{int res;
char a[] = "qwer";
res = strlen(a);
if (res)
printf("The string length is %d",res);
else
printf("The string length is zero!");
system("pause");
return 0;
}
毫无疑问地,打印输出如下。
这个例子很好理解,直接将字符串作为实参传入strlen()
函数中,函数内部进行判断,如果没到字符串结尾,就将长度值加一,并向后移动一个元素的距离,直到访问完每个元素,然后返回字符串长度即可。
int main()
{char ch = 'a';
char *cp = &ch;
int a[] = {0,1,2,3,4,5,6};
int *p = a;
printf("%d", *p + 3);
system("pause");
return 0;
}
打印输出:
int main()
{char ch = 'a';
char *cp = &ch;
int a[] = {0,1,2,3,4,5,6};
int *p1 = &a[0];
int *p2 = &a[3];
printf("%d", p2 - p1);
system("pause");
return 0;
}
打印输出:
指针之间也可以进行部分关系运算,可以执行的关系运算有<
、<=
、>
、>=
。
举个例子:
int main()
{char ch = 'a';
char *cp = &ch;
int a[] = {0,1,2,3,4,5,6};
int *vp = NULL;
for(vp = &a[0]; vp<= &a[6]; vp++)
printf("%d\n", *vp);
system("pause");
return 0;
}
打印输出如下:
其实这里的指针充当了迭代器的角色,将数组中的数值一个个地取出来。
---------------------------------------------------------------------------------END------------------------------------------------------------------------------
你是否还在寻找稳定的海外服务器提供商?创新互联www.cdcxhl.cn海外机房具备T级流量清洗系统配攻击溯源,准确流量调度确保服务器高可用性,企业级服务器适合批量采购,新人活动首月15元起,快前往官网查看详情吧
我们在微信上24小时期待你的声音
解答本文疑问/技术咨询/运营咨询/技术建议/互联网交流