主要回顾 C 数据类型、C 变量、C 常量三个知识点。
# 概念
- C 的令牌:可以是关键字、标识符、常量、字符串值,或者是一个符号
- 分号结束
- 注释
- 单行注释 //,/* 单行注释 */
- 多行注释:/* 多行注释 */
- 标识符: 区分大小写
- 关键字 (保留字): break,case,const,double,enum,... (C99 新增关键字,C11 新增关键字)
- 空格
- 一个正规程序可以有多个函数,但是有且只有一个主函数
- 函数只有在被调用的时候才执行,主函数由系统调用执行
- 数据类型:
- 基本类型 (算术类型)
- 整数类型
- 浮点类型
- 枚举类型 (算术类型:只能赋予其一定的离散整数值的变量)
- void 类型
- 派生类型
- 指针类型
- 数组类型
- 结构类型
- 共用体类型
- 函数类型
- 基本类型 (算术类型)
- 整数类型 (各种类型的存储大小与系统位数和系统类型有关)
类型 | 存储大小 | 值范围 |
---|---|---|
char | 1 字节 | -128 到 127 或 0 到 255 |
unsigned char | 1 字节 | 0 到 255 |
signed char | 1 字节 | -128 到 127 |
int | 2 或 4 字节 | -32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647 |
unsigned int | 2 或 4 字节 | 0 到 65,535 或 0 到 4,294,967,295 |
short | 2 字节 | -32,768 到 32,767 |
unsigned short | 2 字节 | 0 到 65,535 |
long | 4 字节 | -2,147,483,648 到 2,147,483,647 |
unsigned long | 4 字节 | 0 到 4,294,967,295 |
- 可以使用 sizeof 运算符得到对象或类型的存储字节大小
- 浮点类型
类型 | 存储大小 | 值范围 | 精度 |
---|---|---|---|
float | 4 字节 | 1.2E-38 到 3.4E+38 | 6 位有效位 |
double | 8 字节 | 2.3E-308 到 1.7E+308 | 15 位有效位 |
long double | 16 字节 | 3.4E-4932 到 1.1E+4932 | 19 位有效位 |
char:通常是一个字节(八位), 这是一个整数类型。
int:整型,4 个字节,取值范围 -2147483648 到 2147483647。
float:单精度浮点值。单精度是这样的格式,1 位符号,8 位指数,23 位小数。
double:双精度浮点值。双精度是 1 位符号,11 位指数,52 位小数。
c 语言中内存区域划分
- 栈(stack):由编译器进行管理,自动分配和释放,存放函数调用过程中的各种参数、局部变量、返回值以及函数返回地址。操作方式类似数据结构中的栈。
- 堆(heap):用于程序动态申请分配和释放空间。C 语言中的 malloc 和 free,C++ 中的 new 和 delete 均是在堆中进行的。正常情况下,程序员申请的空间在使用结束后应该释放,若程序员没有释放空间,则程序结束时系统自动回收。注意:这里的 “堆” 并不是数据结构中的 “堆”。
- 全局(静态)存储区:分为 DATA 段和 BSS 段。DATA 段(全局初始化区)存放初始化的全局变量和静态变量;BSS 段(全局未初始化区)存放未初始化的全局变量和静态变量。程序运行结束时自动释放。其中 BBS 段在程序执行之前会被系统自动清 0,所以未初始化的全局变量和静态变量在程序执行之前已经为 0。
- 文字常量区:存放常量字符串。程序结束后由系统释放。
- 程序代码区:存放程序的二进制代码。
c 语言中常量又叫字面量,定义后不能进行修改。常量有
- 整数常量:十进制、八进制或十六进制的常量等
- 浮点常量:由整数部分、小数点、小数部分和指数部分组成
- 字符常量:括在单引号中,含转义符
- 字符串常量:括在双引号中
定义常量的方式
- 使用 #define 预处理器。
- 使用 const 关键字。
C 语言中没有专门的字符串类型,因此双引号内的字符串会被存储到一个数组中,这个字符串代表指向这个数组起始字符的指针;
# 代码
# friday.c
#include <stdio.h> | |
#include <float.h> | |
#include <stdlib.h> | |
#include "constants.h" | |
/* | |
演示输出各类型占用存储空间 | |
*/ | |
int main1(){ | |
//% lu 为 32 位无符号整数 | |
//% E 为以指数形式输出单、双精度实数 | |
printf("-------- 各数据类型及其存储字节数 ---------\n"); | |
printf("char 存储字节数 : %lu \n", sizeof(char)); | |
printf("unsigned char 存储字节数 : %lu \n", sizeof(unsigned char)); | |
printf("signed char 存储字节数 : %lu \n", sizeof(signed char)); | |
printf("int 存储字节数 : %lu \n", sizeof(int)); | |
printf("unsigned int 存储字节数 : %lu \n", sizeof(unsigned int)); | |
printf("short 存储字节数 : %lu \n", sizeof(short)); | |
printf("unsigned short 存储字节数 : %lu \n", sizeof(unsigned short)); | |
printf("long 存储字节数 : %lu \n", sizeof(long)); | |
printf("unsigned long 存储字节数 : %lu \n", sizeof(unsigned long)); | |
printf("float 存储字节数 : %lu \n", sizeof(float)); | |
printf("float 最小值: %E\n", FLT_MIN ); | |
printf("float 最大值: %E\n", FLT_MAX ); | |
printf("精度值: %d\n", FLT_DIG ); | |
return 0; | |
} | |
/* | |
演示各类型定义 | |
*/ | |
int s; | |
int t; | |
int m = 16; | |
int n = 17; | |
extern double PI; | |
extern float E; | |
// 尽量不要写这种定义方式,有警告,extern 只做声明,而非定义 | |
//extern int p = 999; | |
int main2(){ | |
// 字符型常量 (含转义字符) | |
char character = 'a'; | |
char character1 = '\n'; | |
//10 进制数 | |
int decimal_num = 12; | |
//8 进制数 | |
int octal_num = 045; | |
//2 进制数 | |
int binary_num = 0b11011; | |
//16 进制数 | |
int hexadecimal_num = 0x21458adf; | |
// 单精度常量 | |
float pi = 3.14f; | |
// 默认为双精度 | |
float e = 2.71828; | |
// 双精度浮点 (double) 型 | |
double g = 9.8; | |
// 字符串常量 | |
char str[5] = "Jalen"; | |
// | |
int i; | |
i = 12; | |
PI = 13; | |
s = 14; | |
t = 15; | |
m = 116; | |
n = 117; | |
E = 666; | |
extern float G; | |
extern int AGE; | |
AGE = 22; | |
printf("i=%d,PI=%f,s=%d,t=%d,m=%d,n=%d,E=%f,G=%f,age=%d", i, PI, s, t, m, n, E, G, AGE); | |
return 0; | |
} | |
//c 没有变量提升 | |
// int m = 16; | |
// int n = 17; | |
// extern int o; | |
// extern int p; | |
/* | |
演示类型转换 | |
*/ | |
int main3(){ | |
// 浮点数赋给整型,该浮点数小数被舍去;下面输出圆周长 = 37 | |
float pi = 3.1415926; | |
int circleRadius = 6; | |
int circlePerimeter = 2 * pi * circleRadius; | |
printf("圆周长是 %d \n", circlePerimeter); | |
// 整数赋给浮点型,数值不变,但是被存储到相应的浮点型变量中; | |
int age = 20; | |
int monthPerYear = 12; | |
float totalMonth = age * monthPerYear; | |
printf("你已存活了 %f 个月\n", totalMonth); | |
// 向下取整 | |
float weight = 8.8; | |
int intWeight = (int)8.8; | |
printf("重量是 %d KG\n", intWeight); | |
// 两个整数相除,结果仍为整数 2,把 2 赋给浮点数 f | |
int num1 = 10; | |
int num2 = 4; | |
float num3 = num1/num2; | |
printf("商是 %f \n", num3); | |
// 求和 | |
float a = 2.8, b = 2.9; | |
float c = a + b; | |
int d = a + b; | |
printf("和转浮点数是 %f \n", c); | |
printf("和转整数是 %d \n", d); | |
} | |
/* | |
输出变量地址 | |
内存寻址 | |
*/ | |
int main4(){ | |
int a; | |
int b; | |
int c; | |
int d; | |
// 变量地址的获取方式:& 变量名 | |
//a 的地址是 00000015667ffafc | |
//b 的地址是 00000015667ffaf8 | |
//c 的地址是 00000015667ffaf4 | |
//d 的地址是 00000015667ffaf0 | |
//a 的地址比 b 的地址大 4 字节,依次类推 | |
printf("a的地址是%p\nb的地址是%p\nc的地址是%p\nd的地址是%p\n",&a,&b,&c,&d); | |
//a 的值是 0 | |
printf("a的值是%d\n",a); | |
return 0; | |
} | |
/* | |
C 语言经过编译之后将内存中的区别 | |
*/ | |
int k1 = 1; | |
int k2; | |
static int k3 = 2; | |
static int k4; | |
int main5(){ | |
static int m1 = 2, m2; | |
int i = 1; | |
char *p; | |
char str[10] = "hello"; | |
char *q = "hello"; | |
p = (char *)malloc(100); | |
free(p); | |
printf("栈区-变量地址 i:%p\n", &i); | |
printf(" p:%p\n", &p); | |
printf(" str:%p\n", str); | |
printf(" q:%p\n", &q); | |
printf("堆区地址-动态申请:%p\n", p); | |
printf("全局外部有初值 k1:%p\n", &k1); | |
printf(" 外部无初值 k2:%p\n", &k2); | |
printf("静态外部有初值 k3:%p\n", &k3); | |
printf(" 外静无初值 k4:%p\n", &k4); | |
printf(" 内静态有初值 m1:%p\n", &m1); | |
printf(" 内静态无初值 m2:%p\n", &m2); | |
printf("文字常量地址 :%p, %s\n",q, q); | |
printf("程序区地址 :%p\n",&main5); | |
return 0; | |
} | |
/* | |
等号赋值 | |
*/ | |
int main6(){ | |
int a, b; | |
// 等同 a=b=3 | |
a = (b = 3); | |
printf("%d\n",a); | |
printf("%d\n",b); | |
return 0; | |
} | |
/* | |
定义常量 | |
(1)#define 定义常量格式:#define identifier value | |
(2)注意使用 #define 定义常量没有 “=” 号赋值,而且结尾没有分号 | |
(3)#define 是宏定义,它不能定义常量,但可以实现在字面意义上和其它定义常量相同的功能 | |
(4)define 定义的是不带类型的常数,只进行简单的字符替换。在预编译的时候起作用,不存在类型检查。 | |
(5)const 定义常量格式:const type variable = value; | |
(6)const 定义的是变量不是常量,const 只是把该变量所占的内存变为只读 | |
(7)区别: | |
- 编译器处理方式不同:#define 宏是在预处理阶段展开;const 常量是编译运行阶段使用 | |
- 类型和安全检查不同:#define 宏没有类型,不做任何类型检查,仅仅是展开;const 常量有具体的类型,在编译阶段会执行类型检查 | |
- 存储方式不同:#define 宏仅仅是展开,有多少地方使用,就展开多少次,不会分配内存;const 常量会在内存中分配 (可以是堆中也可以是栈中) | |
- const 可以节省空间,避免不必要的内存分配(内存地址 & amp; 立即数,#define 在内存中有若干个拷贝) | |
- const 提高了效率 | |
- #define 宏替换只作替换,不做计算,不做表达式求解 | |
- const 定义变量时就需要初始化赋值 | |
*/ | |
#define LENGTH 5 | |
#define WIDTH 10 | |
#define NEWLINE '\n' | |
int main7(){ | |
int area; | |
area = LENGTH * WIDTH; | |
printf("value of area : %d", area); | |
printf("%c", NEWLINE); | |
const int LENGTH1 = 20; | |
const int WIDTH1 = 10; | |
const char NEWLINE1 = '\n'; | |
area = LENGTH1 * WIDTH1; | |
printf("value of area : %d", area); | |
printf("%c", NEWLINE1); | |
return 0; | |
} | |
/* | |
define 注意 “边缘效应” | |
*/ | |
#define N 2+3 | |
#define M (2+3) | |
int main8(){ | |
printf("N 的值为 : %d\n", N); | |
printf("M 的值为 : %d\n", M); | |
double a, b; | |
a = (float)N / (float)2; // 2+3/2=2+1.5=3.5 | |
b = (float)M / (float)2; // (2+3)/2=2.5 | |
printf("a 的值为 : %.2f\n", a); | |
printf("b 的值为 : %.2f\n", b); | |
return 0; | |
} |
# constants.h
double PI = 3.1415926; | |
float E = 2.71828; | |
float G = 9.8; | |
int AGE = 12; | |
int plus(int x, int y){ | |
return x + y; | |
} |