数据类型

char

  • char-字符型,一个字节的整数,范围【-128-127】
  • 字符数据(单引号括起来的字符)的本质是整数类型
  • char 类型的变量可以打印为整数或者字符

const

  • const 修饰的变量是只读的,本质还是变量
  • const 修饰的局部变量在上分配空间
  • const 修饰的全局变量在只读存储区分配空间
  • const 只在编译器有用,在运行期无用

const 修饰的变量不是真正的变量,他只是告诉编译器该变量不能出现在赋值符号的左边;

  • 在现代 C 语言编译器中,直接修改 const 全局变量将导致程序崩溃

注意:标准 C 语言编译器不会将 const 修饰的全局变量存储与只读存储区中,而是存储与可修改的全局数据区,其值依然可以改变;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include<stdio.h>

const int g_cc = 2;

void main() {
const int cc = 1;
int p = 0;
int* pp = (int*)&cc;
int* g_pp = (int*)&g_cc;

printf("%d\n", cc); // 1

//p = 3 // Error
*pp = 3;
printf("cc = %d\n", cc);// 3

printf("g_cc = %d\n", g_cc);// 2
//g_cc = 2 // Error

*g_pp = 3; // oops! Segmentation fault
printf("g_cc = %d\n", g_cc);// 2

}

Gcc 将全局 cosnt 存储与只读存储区,当我们试图修改其中的内容时候,就会报 Segmentation fault 错误(这也是标准 C 语言的定义)

其他编译器可能就没问题

  • C 语言中的 const 使得变量具有只读属性
  • 现代 C 编译器中的 const 将具有全局生命周期的变量存储与只读存储区
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
#include<stdio.h>

const int g_array[5] = {0};

void modify(int* p, int v) {
*p = v;
}

void main() {
int const i = 0;
const static int j = 0;
int const array[5] = {0};

modify((int*)&i, 1); // ok
modify((int*)&j, 1); // Error
modify((int*)&array[0], 1); // ok
modify((int*)&g_array[0], 1); // Error

printf("i = %d\n", i);
printf("i = %d\n", j);
printf("arrya[0] = %d\n", array[0]);
printf("g_arrya[0] = %d\n", g_array[0]);

}

modify((int*)&j, 1); // Error

被 static 修饰的变量被存储在全局存储区,const 修饰的全局变量将被存放在只读存储区

modify((int*)&g_array[0], 1); // Error

g_array[0] 是全局变量,被 const 修饰的全局变量将存储区只读存储区

  • const 修饰函数参数表示在函数体内不希望改变参数的值
  • const 修饰函数返回值表示返回值不可改变,多用于返回指针的情形

C 语言中的字符串字面量存储与只读存储区中,在程序中需要使用 const char* 指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include<stdio.h>

const char* f(const int i ) {
// i = 5;
return "kay wang";
}

void main() {
const char* pc = f(0);
printf("%s\n", pc);

//pc[3] = "_"; // Error 因为是只读的

printf("%s\n", pc);

}

void

在 C 语言中可以定义 void 类型的指针

1
2
3
4
5
6
7
#include<stdio.h>

int main() {
void var; // Eroor
void array[5]; // Err
void* pv; // C 语言允许定义 void 类型的指针
}

不存在 void 类型的变量

  • C 语言中没有定义 void 究竟是多大内存的别名,无法在内存中开辟出 void 大小对应的变量

  • 而指针是固定 4 或 8 个字节大小的,所以可以开辟指针类型的 void 变量

注意:在非标准 C 语言编译器中有的对 void 规定了大小,比如 gcc 编译器就规定了 void 大小为 1 个字节

printf(“%ld\n”, sizeof(void)); // 1

意义

  • C 语言规定只有相同类型的指针才可以互相赋值
  • void* 指针作为左值用于 “接收” 任意类型的指针
  • void* 指针作为右值使用时需要进行强制类型转换
1
2
3
4
5
6
7
8
9
10
11
12
13
14
// malloc() 函数是 C 语言标准库中的一个函数,用于动态地分配内存。
int* pI = (int*)malloc(sizeof(int));
char* pC = (char*)malloc(sizeof(char));
void* p = NULL;
int* pni = NULL;
char* pnc = NULL;

p = pI; // ok
============
pni = P; // Error oops! pni = (int*)p

p = pC // ok
==================
pnc = P; // Error oops! pnc = (void*)p
  • void 是一种抽象的数据类型
  • void 类型不能用于定义变量
  • void 类型用于声明函数无参数
  • void 类型用于声明函数无返回值
  • 可以定义 void* 类型的指针
  • void* 类型的指针可以接收任意类型的指针值

volatile

  • volatile 可理解为 “编译器警告关键字”
  • volatile 告诉编译器必须每次去内存中取变量值
  • volatile 主要修饰可能被多个线程访问的变量
  • volatile 也可以修饰可能被未知因数更改的变量

常量

C 语言中的常量类型

  • 字面量
    • 直接表示值含义的符号,如:5、’a’、”Ubuntu”
  • 宏常量(字符化的字面量)
    • 通过 #define 定义,间接表示值的符号,如:FIV => 5.5
  • 枚举常量
    • 通过 enum 定义,间接表示值的符号,如:First => 1
1
2
3
4
5
6
7
8
// 示例
#define NAME kay;

// 枚举常量只能是整形常量,浮点数都不可以
enum {
ThirdValue = 333,
FourthValue = 444,
};

常量的类型

  • 字面量有其默认的类型,也可通过后缀指定其类型
  • #define 定义的宏常量可以是任意类型
  • enum 定义的枚举常量只能是整形

字面量是常量,但是它的默认类型由语言提供,也可通过后缀指定其类型;

const

C 语言中的只读变量

  • C 语言提供了 const 关键字,用于修饰一个变量
  • 被 const 修饰的变量只能作为右值使用
    • 无法通过赋值操作改变 const 变量的值
    • const 修饰

浮点数

同样是4个字节,为什么float却比int的范围大得多呢?

  • float 能表示的具体数字的个数与int相同
  • float可表示的数字之间不是连续的,存在间隙
  • float只是一种近似的表示法,不能作为精确使用
  • 由于内存表示法相对复杂,float的运算速度比int慢很多

注意:

double 与 float 具有相同的内存表示法,因此 double 也是不精确的。由于 double 占用的内存较多,所能表示的精度比 float 高;

小结

  • 浮点类型与整数类型的内存表示法不同
  • 浮点类型的内存表示更复杂
  • 浮点类型可表示的范围更大
  • 浮点类型是一种不精确的类型
  • 浮点类型的运算速度很慢

类型转换

整形

long

  • long 在使用不同的编译器时,可能占用的内存不同
  • long 通常占 4 字节内存,也可能占 8 字节内存
  • long long表示整形,固定占用 8 字节内存
  • long long 是 long long int 的缩写形式

struct

  • C 语言中的 struct 可以看作变量的集合

空结构体占用多大内存?

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// 示例
#include <stdio.h>

struct TS {

};

int main() {

struct TS t1;
struct TS t2;

printf("sizeof(struct TS) = %ld\n", sizeof(struct TS));
printf("sizeof(t1) = %ld, &t1 = %p\n", sizeof(t1), &t1);
printf("sizeof(t2) = %ld, &t2 = %p\n", sizeof(t2), &t2);

/*
sizeof(struct TS) = 0
sizeof(t1) = 0, &t1 = 0x7ffec502f4d6
sizeof(t2) = 0, &t2 = 0x7ffec502f4d7
*/

return 0;
}

GCC 编译器允许空结构体的存在,且大小为 0 ,其他编译器如 vs 则不支持空结构体的存在;

柔性数组

  • 柔性数组即数组大小待定的数组
  • C 语言中可以由结构体产生柔性数组
  • C 语言中结构体的最后一个元素可以是未知大小的数组
1
2
3
4
5
6
struct SoftArray {
int len;
int array[];
};
// 此时:
sizeof(struct SoftArray) = ?

SoftArray 中的 array 仅是一个待使用的标识符,不占用存储空间;

此时 SoftArray 的大小就是 int len 的大小 4 个字节;

柔性数组的用法