精通C语言extern char k[] != extern char *k

Posted 从善若水

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了精通C语言extern char k[] != extern char *k相关的知识,希望对你有一定的参考价值。

本人就职于国际知名终端厂商,负责modem芯片研发。
在5G早期负责终端数据业务层、核心网相关的开发工作,目前牵头6G算力网络技术标准研究。


博客内容主要围绕:
       5G协议讲解
       算力网络讲解(云计算,边缘计算,端计算)
       高级C语言讲解
       Rust语言讲解

extern char k[] 不等于 extern char *k

       我们先看一个例子,大家就知道标题的含义了:

// file 1

unsigned long long CSRS[5]={0};

/* other code */
// file 2

extern unsigned long long *CSRS;

/* 其它引用变量CSRS的code */

上面的code对吗?

有些书籍会告诉你数组和指针大部分情况下是一样的,可以相互转换(interchangeable)使用,但是并没有告诉你什么情况下不可以随便转换使用。上面的例子其实是错误的,我们在这篇博文中向你介绍在上面code的情况下指针不等于数组

下面看一个具体的例子:

// file 1

#include<stdio.h>
#include<stdlib.h>

extern void print_CSRS();

unsigned long long CSRS[5]={0};

int main()
{
	/* 打印数组首元素的地址 */
	printf("%s CSRS :%p\\n",__func__,CSRS);
	/* 打印数组的地址 */
	printf("%s &CSRS :%p\\n",__func__,&CSRS);
	
	/* 使用calloc是为了分配的5个elements在逻辑地址上连续 */
	unsigned long long *temp = (unsigned long long*)calloc(5,sizeof(unsigned long long));
	
	for(int i=0;i<5;i++)
	{
		temp[i] = 123 + i;
		CSRS[i]= (unsigned long long)(temp+i);
		/* 按照指针格式打印 */
		printf("%s CSRS[%d] :%p\\n",__func__, i, CSRS[i]);
		/* 按照 unsigned long long 格式打印 */
		printf("%s CSRS[%d] :%llu\\n",__func__, i, CSRS[i]);
	}
	
	printf("\\n");
	
	print_CSRS();
	
	return 0;
}
// file 2

#include<stdio.h>

//extern unsigned long long CSRS[];
extern unsigned long long* CSRS;

void 
print_CSRS()
{
	printf("%s CSRS :%p\\n",__func__,CSRS);
	printf("%s &CSRS :%p\\n",__func__,&CSRS);
	
	for(int i=0;i<5;i++)
	{
		printf("%s &CSRS[%d] :%p\\n",__func__, i, CSRS+i);
		printf("%s CSRS[%d] :%llu\\n",__func__, i, CSRS[i]);
	}
}

我们看运算结果:
在这里插入图片描述
我们这里先不解释为什么,下面会慢慢解释。我们再看一个正确的code,我们修改file 2:

// file 2

#include<stdio.h>

extern unsigned long long CSRS[];
//extern unsigned long long* CSRS;

void 
print_CSRS()
{
	printf("%s CSRS :%p\\n",__func__,CSRS);
	printf("%s &CSRS :%p\\n",__func__,&CSRS);
	
	for(int i=0;i<5;i++)
	{
		printf("%s &CSRS[%d] :%p\\n",__func__, i, CSRS+i);
		printf("%s CSRS[%d] :%llu\\n",__func__, i, CSRS[i]);
	}
}

正确的运行结果如下:
在这里插入图片描述
想要搞清除这个问题我们先要了解C语言变量的定义与声明的区别。


一、定义(Definition)与声明(Declaration)的区别

对象(object):C语言ISO标准中将对象一词解释为一个大小合适的内存块,这与C++的对象一词含义不同。
int a;//表示创建一个sizeof(int)大小的对象

定义(Definition)只能出现在项目中的一个位置定义用来创建一个新的对象;确定对象的类型;预留内存
声明(Declaration)可以出现在项目中的多个地方用来描述一个对象的类型;用来引用一个定义在其它位置的对象

定义是一种特殊的声明,它会为一个对象分配内存


二、编译器如何解释"extern unsigned long long CSRS[]"

       每一个变量的地址在编译期就已经确定了。编译器对一个数组的引用可以用下图进行解释:

extern unsigned long long CSRS[]
  • 编译器查询符号表找到变量 CSRS的地址 100(我们假设地址为100)
  • 在程序运行期间获取变量i 的值,并于100相加
  • 直接获取地址(100+i)处的值
    在这里插入图片描述

三、编译器如何解释"extern unsigned long long *CSRS"

       指针变量解引用(dereference)只能发生在运行期间。使用“extern unsigned long long *CSRS”其实就是告诉编译器CSRS是一个指针,这个指针指向的对象存储着一个类型为unsigned long long的值,要想获取这个值需要下面的步骤:

extern unsigned long long *CSRS
  • 编译器查询符号表找到变量 CSRS的地址 100(我们假设地址为100)
  • 获取地址100处的值5460(我们假设指针指向地址5460处)
  • 再从地址5460处获取我们想要的值
    在这里插入图片描述

四、重看上面的code

       根据上面code的输出制作下图:
在这里插入图片描述
所以会产生下面的结果:
在这里插入图片描述

慢慢理解吧,毕竟C的精华就在指针上!!!


在这里插入图片描述

以上是关于精通C语言extern char k[] != extern char *k的主要内容,如果未能解决你的问题,请参考以下文章

求c语言读取写入文本文件的函数实现

undefined reference to `recvIpcMsg(int, ipc_msg*)'——#ifdef __cplusplus extern "C" { #e

易语言PostMessage

汉诺塔算法学习-C代码

C语言 extern “C”

嵌入式Linux从入门到精通之第二节:语言基础