注释
定义:只提示不执行不编译
单行注释://注释信息
多行注释:/*注释信息*/
关键字:被赋予特定含义的单词组合都是小写的,在定义变量与常量时不能使用关键字。
常量:在程序运行的过程中不能改变的数据。
基本常量
整型常量:所有常量。
实性常量:所有小数,(0.32可以写出 '.32'还有3.0可以写成'3.')。
字符常量:包括字母,数字,英文符号,要用单引号括起来并且只能是一个,这与Java的不一样Java里可以写一个中文,与编码有关。
字符串常量:可以写多个字母,数字,英文符号,用双引号括起来。
宽字符(如中文等)用 wchar_t时,使用 %lc 并传 wchar_t;字符串用 %ls。
注:除了字符串常量可以直接输出外,其他常量都要用占位符输出(%s,%d,%f,%c)其中%s是字符串常量,%c 用于输出单个字符。
示例:
#include <stdio.h>
#include <wchar.h>
#include <locale.h>
int main() {
printf("%d",2);//??:2
printf("%d",'2');//??:50(????)
printf("%c",65);//??:A((????))
printf("%d",'A');//??:65(????)
printf("main");
printf("%s","main");
setlocale(LC_ALL, "");/*C 标准库函数(在 <locale.h>),用于设置/查询程序的"本地化环境"(locale)。
影响:字符分类与大小写、字符串比较/排序、数字小数点、货币格式、日期时间名称、多字节/宽字符处理与显示等。*/
wchar_t ch = L'你';
wchar_t *chl = L"你好";
const wchar_t *ws = L"你好,世界";
wprintf(L"%lc\n", ch);
wprintf(L"%ls\n", chl);
wprintf(L"%ls\n", ws);
return 0;
}
注:const 是限定符:表示"通过这个标识符不可修改"。它让对象只读、接口更安全。不等于绝对不可变:对原本是可写内存的对象,强行"去 const 后修改"可能导致未定义行为;对本就只读的对象(如字符串字面量)修改必然是未定义行为。
补充:windows 换行符 r\n macos \r Linux \n(不管在任何操作系统中换行符写\n就行了系统会自动改变)
变量
定义:储存数据的容器
定义格式:数据类型 变量名 ;(C语言中是以分号结尾的表示该语句的结束并且用大括号表示层级关系)使用的时候就直接变量名 = 数据值;
补充:C默认数据类型是int,不过在定义int类型的时候还是要加int,C99 及之后的标准要求显式声明类型,不过有些情况可以省略比如long int可以简化成long
注:一个变量只能存储一个值,变量名不允许重复定义,一条语句可以定义多个变量,变量使用的时一定要进行赋值,还要注意变量的作用范围。
示例:
#include <stdio.h>
#include <wchar.h>
#include <locale.h>
int main() {
long int count ;long coun;
count = 10;
coun = 9;
printf("%d",count);
printf("%d",coun);
return 0;
}
数据类型
作用:1,变量能存储什么类型的数据,2,存储的大小
整数类型
标准整数类型
// 有符号整数
char c; // 通常 1 字节,范围:-128 到 127(或 0 到 255)
short s; // 至少 2 字节,通常:-32,768 到 32,767
int i; // 至少 2 字节,通常 4 字节,范围:-2^31 到 2^31-1
long l; // 至少 4 字节,通常 4 或 8 字节
long long ll; // 至少 8 字节,C99 引入
// 无符号整数
unsigned char uc; // 0 到 255
unsigned short us; // 0 到 65,535
unsigned int ui; // 0 到 4,294,967,295(32位)
unsigned long ul; // 0 到 很大
unsigned long long ull;// 0 到 非常大
类型大小(sizeof)
整数类型的确切大小依赖于编译器和平台
#include <stdio.h>
#include <limits.h>
int main() {
printf("类型大小(字节):\n");
printf("char: %zu\n", sizeof(char));//1
printf("short: %zu\n", sizeof(short));//2
printf("int: %zu\n", sizeof(int));//4
printf("long: %zu\n", sizeof(long));//4
printf("long long: %zu\n", sizeof(long long));//8
printf("\n典型范围:\n");
printf("char: %d to %d\n", CHAR_MIN, CHAR_MAX);//-128 to 127
printf("int: %d to %d\n", INT_MIN, INT_MAX);//-2147483648 to 2147483647
printf("unsigned int: 0 to %u\n", UINT_MAX);//0 to 4294967295
return 0;
}
整数常量表示
不同进制表示
int decimal = 42; // 十进制
int octal = 052; // 八进制(以0开头)
int hex = 0x2A; // 十六进制(以0x开头)
int binary = 0b101010; // 二进制(C23标准,有些编译器扩展支持)
// 输出
printf("十进制: %d\n", decimal); // 42
printf("八进制: %o, 十进制值: %d\n", octal, octal); // 52, 42
printf("十六进制: %x, 十进制值: %d\n", hex, hex); // 2a, 42
类型后缀
int a = 42; // int 类型
long b = 42L; // long 类型(L 或 l)
long long c = 42LL; // long long 类型
unsigned int d = 42U; // unsigned int 类型
unsigned long e = 42UL; // unsigned long 类型
// 八进制和十六进制也可以加后缀
long hex_long = 0x2AL; // long 类型
unsigned hex_unsigned = 0x2AU; // unsigned 类型
有符号vs无符号
默认无符号
#include <stdio.h>
#include <limits.h>
int main() {
signed char sc = -128; // 有符号:-128 到 127
unsigned char uc = 255; // 无符号:0 到 255
signed int si = -2147483648; // 最小负值
unsigned int ui = 4294967295U; // 最大正值
// 溢出行为不同
unsigned int u = 0;
printf("无符号 0 - 1 = %u\n", u - 1); // 4294967295(回绕)
int s = INT_MIN;
printf("有符号最小值 - 1 = %d\n", s - 1); // 未定义行为或回绕
// 混合运算
int x = -5;
unsigned int y = 10;
if (x > y) { // 危险:x 会被转换为 unsigned int
printf("x > y\n"); // 可能输出,因为 -5 变成很大的正数
}
return 0;
}
隐式转换与显式转换
#include <stdio.h>
int main() {
char c1 = 100, c2 = 100;
int result;
// 整数提升:char 运算前提升为 int
result = c1 * c2; // 100 * 100 = 10000(char会溢出,但int不会)
printf("char乘法: %d\n", result); // 10000
char c3 = c1 * c2; // 溢出!10000 > 127
printf("char赋值: %d\n", c3); // 截断的结果
// 混合类型运算
int i = 1000;
short s = 100;
long long ll = i * s; // i和s都提升为int,然后转换
printf("混合运算: %lld\n", ll);
// 显式类型转换
double d = 3.14159;
int int_part = (int)d; // 3
printf("浮点转整数: %d\n", int_part);
return 0;
}
固定宽度整数类型(C99)
#include <stdio.h>
#include <stdint.h> // 固定宽度整数
#include <inttypes.h> // 格式化输出宏
int main() {
// 精确宽度类型
int8_t i8 = 127; // 正好 8 位有符号
uint8_t u8 = 255; // 正好 8 位无符号
int16_t i16 = 32767; // 正好 16 位有符号
uint16_t u16 = 65535; // 正好 16 位无符号
int32_t i32 = 2147483647; // 正好 32 位有符号
uint32_t u32 = 4294967295U; // 正好 32 位无符号
int64_t i64 = 9223372036854775807LL; // 64位有符号
uint64_t u64 = 18446744073709551615ULL; // 64位无符号
// 最小宽度类型(至少指定宽度)
int_least8_t least8; // 至少 8 位
uint_fast32_t fast32; // 系统处理最快的至少 32 位类型
// 最大宽度类型
intmax_t biggest_signed; // 最大的有符号整数类型
uintmax_t biggest_unsigned; // 最大的无符号整数类型
// 使用 PRId64 等宏进行格式化输出
printf("int64_t: %" PRId64 "\n", i64);
printf("uint64_t: %" PRIu64 "\n", u64);
return 0;
}
整数运算的陷阱
#include <stdio.h>
#include <limits.h>
int main() {
// 1. 整数溢出
int max_int = INT_MAX;
printf("INT_MAX = %d\n", max_int);
printf("INT_MAX + 1 = %d(溢出)\n", max_int + 1);
// 2. 符号扩展问题
char negative_char = -5;
unsigned char positive_uchar = 251; // 也是 -5 的二进制表示
printf("有符号char -5: %d\n", negative_char);
printf("无符号char 251: %u\n", positive_uchar);
// 当赋值给 int 时
int i1 = negative_char; // -5(符号扩展)
int i2 = positive_uchar; // 251(零扩展)
printf("扩展后:%d vs %d\n", i1, i2);
// 3. 移位运算
int x = 1;
printf("1 << 31 = %d\n", x << 31); // 可能溢出
unsigned int ux = 1U;
printf("1U << 31 = %u\n", ux << 31); // 2147483648
// 4. 除零错误
int zero = 0;
// printf("10 / 0 = %d\n", 10 / zero); // 运行时错误
return 0;
}
最佳实践
// 1. 明确指定类型大小
#include <stdint.h>
// 使用固定宽度类型进行二进制数据交换
uint32_t network_value; // 明确 32 位
// 2. 使用 size_t 表示大小和索引
#include <stddef.h>
size_t array_size = 1000;
// 3. 处理大数时使用 long long
long long big_sum = 0LL;
for (int i = 0; i < 1000000; i++) {
big_sum += i; // 避免溢出
}
// 4. 无符号数用于位操作和模运算
unsigned int flags = 0;
flags |= 0x01; // 设置位
flags &= ~0x02; // 清除位
// 5. 使用宏安全地检查溢出
#define ADD_WILL_OVERFLOW(a, b, type) \
((b) > 0 && (a) > (type)MAX - (b)) || \
((b) < 0 && (a) < (type)MIN - (b))
// 6. 优先使用有符号类型进行算术运算
int32_t calculate_sum(int32_t a, int32_t b) {
return a + b; // 编译器更容易优化和检查
}
平台差异处理
// 条件编译处理不同平台的整数大小差异
#if INT_MAX == 32767
// 16位 int 系统
typedef long int32_t;
#elif INT_MAX == 2147483647
// 32位 int 系统
typedef int int32_t;
#elif INT_MAX == 9223372036854775807
// 64位 int 系统
typedef int int32_t; // 可能还需要特殊处理
#endif
// 或者直接使用 stdint.h 的可移植类型
#include <stdint.h>
整数类型的选择取决于:
- 所需范围:确保类型能容纳所有可能的值。
- 内存考虑:大数组中选择较小的类型。
- 性能考虑:使用处理器的自然字长。
- 可移植性:使用固定宽度类型进行数据交换。
- 符号需求:明确是否需要负数。
今天第二篇,为什么要写的这么清楚呢?因为都是我的教训,后面在学习内存的时候还是要学的。