⚠️数组名和指针常量类似,但并非指针!(也由于是指针常量,因此数组名也是un-assignable的)程序员
只有两种状况下数组名不用指针常量来表示:数组
sizeof
:对数组名使用sizeof
将返回整个数组的长度(以字节为单位)&
取址:获得指向数组的指针(而不是指向第一个元素的指针的指针)经过指针来访问数组中的元素可能比用下标的效率更高,由于经过下标访问数组老是要作如下运算:安全
arr + (index * sizeof(elementOfArr)) // 此处的乘法没法避免
而某些状况(主要是循环体)下,对该偏移值的计算将被编译器优化为纯加法(一个在现代基本没什么用的知识。。):函数
for (int* arrPtr = arr; arrPtr < arr + 10; arrPtr ++) { ... } // arrPtr++ 所加上的值只须要计算第一次
指针不只在作加法时须要考虑元素大小,两指针相减的结果也会被经过除法转换为元素个数(单纯比较是否相等则不须要)优化
⚠️函数接收多维数组作参数时,必须指定除第一维外其余全部维度的大小,不然寻址时函数没法计算偏移量指针
void func(int x, int y, int arr[][]) { // 未指定第二维大小 printf("%d", arr[x][y]); // 访问arr的第(x * sizeOfRow + y)个元素,但函数不知道sizeOfRow }
字符数组的初始化与字符串常量code
char msg[] = "Hello"; char *msg = "Hello"; // 字符串常量是read only的
⚠️声明一个指向数组的指针:内存
int arr[3][10]; int (*arrPtr)[10] = arr; // arrPtr是一个指向拥有10个int元素的数组的指针,arrPtr + 1将指向下一行的10个int的开始
多维数组能够经过如下形式传递给函数:element
void func(int (*mat)[10]); void func(int mat[][10]);
但当多维数组各维度的大小并不总固定时(个人最爱~),能够经过如下方式将它以一维数组的形式传递给函数:字符串
int *arrPtr = &arr[0][0]; int *arrPtr = arr[0];
可是如下这种声明可能会致使严重的问题:
int arr[3][10]; int (*arrPtr)[] = arr; // 此时指针arrPtr不知道数组arr的size信息,它将会把0看成arr的二维宽度(而不是3)
下标引用[]
的优先级高于间接访问*
int *p[10]; // p被声明为拥有10个int*元素的数组
strlen
的返回值为size_t
,是一个unsigned值(永远大于等于0)NULL
结尾(在必要的状况下)且大小合适strcpy
、strcat
等函数会将目标字符串的地址做为返回值,这个特性令它们能够被嵌套使用strerror
函数接收一个错误代码,并返回一个指向描述该错误的字符串的指针⚠️struct
(忽然发现我根本不知道struct
的标准语法。。)
struct Tag { // struct关键字和花括号之间的部分为该struct的标签,为可选部分,这样声明的结构体每次使用时都要加上struct关键字 int val; char name[10]; } tag1, *tag2; // 此处能够顺便声明该类型的变量,但此后声明新变量必须使用struct Tag tag;,由于Tag实际上不是一种类型 typedef struct { // 将这种struct定义为一种类型,所以之后使用时不用加struct关键字 int val; char name[10]; } Tag; // 之后能够直接使用Tag tag;来声明新变量
因此struct {};
才是一个总体,它能够被加上标签做为记号,也能够被typedef
为一种新的结构体
(我居然一直觉得是在主函数外声明新变量就要在类型前加struct关键字。。因此我这么久都写的啥==)
点操做符.
的优先级高于间接访问*
,因此C提供了->
来更为方便地经过结构体指针访问它的成员(原来如此!!)
(*ptr).val = 0; ptr->val = 0; // 显然下面这种写法更为直观
以及优先级上->
高于.
高于*
:
*p->a.b; // 将会从p指向的结构体中取出a结构体的b成员并对它进行间接访问,即*((p->a).b)
不完整声明:使两个(或更多)结构体能够互相包含对方的指针
struct B; // 提早声明B的存在 struct A { struct B *bPtr; ... }; struct B { struct A *aPtr; ... };
typedef struct foo foo;
是合法的了,可是这种写法在以前的编译器上(可能!)会出现兼容性问题⚠️C中的空结构体为undefined行为(但C++容许结构体为空),以及结构体实际上容许嵌套声明,所以可能无心间声明空结构体:
struct A { struct B { // 此处实际只是struct B的定义,而非变量声明。所以没有其余成员的struct A实际为空结构体 int id; ... }; };
sizeof
将返回它实际占用的字节数,包括那些为了边界对其而跳过的字节。所以要肯定一个成员对于结构体起始位置的实际偏移量,应使用offset(type, member)
宏(位于stddef.h
),type
为结构类型,member
为该成员的名字。这个宏将返回一个size_t
值
位段(bit filed):(本质上是不可移植的。。hmmm)
int
或unsigned int
类型struct CHAR { unsigned ch : 7; unsigned font : 16; unsigned size : 19; }; struct CHAR c1;
⚠️联合(union
):(经过访问不一样的联合成员,内存中的同一块内容能够被解释为不一样的东西)
与struct
类似,union
也容许嵌套声明
struct POINTER { // 实际包含两个变量,type和ptr enum {INT, FLOAT, STRING} type; union { int *i; float *f; char *s; } ptr; // 匿名union };
初始化:初始值必须为第一个成员的类型,且必须用花括号包围
union { int a; float b; } x = { 5 }; // 其余类型的初始值(若是可能的话)将会被转换为一个int赋值给x.a