C 小知识
gcc hello.c -o hello_world # 编译时指定生成的文件名
int a; // 定义一个变量但没有赋值,那么它的值是随机的.但是当我们定义一个全局变量并没有初始化,那么它的值是0.
字节: 一个字节 == 8个二进制位
单引号内是字符, 双引号内是字符串(字符串的结尾符是 ‘\0’, 战用1个字节)
- 基本数据类型, 运算符号, 关键字
int: 整形, 4字节
short: 短整形, 2字节
long: 长整形, 64位unix系统下占用8字节
unsigned int: 无符号整形,效率会高一些,表示范围会大一些,处理非数学运算时使用
unsigned long
unsigned short
unsigned char
float: 小数点后6位
double: 小数点后16位
char: 字符类型,使用ASCII编码
&&: 逻辑与
||:逻辑或
sizeof: 返回变量或数据类型占用的内存的长度,以字节位单位
&:取地址符,加在某个变量前面,组合后的符号代表这个变量的地址值
指针: 指针全称是指针变量,其实质是C语言的一种变量。它的值通常是某个变量的地址值(p = &a), 然后我们可以使用 *p 去间接访问p指向的那个变量
void: 代表任意类型(不是空类型),任意类型的意思不是哪种类型都行,而是说 类型是未知的,还没有指定。
一个函数的参数列表是void,代表这个函数调用时不需要(不是不能)给它传参
返回值类型是void, 代表这个函数不会返回一个有意义的返回值
void * : 表示void 类型的指针,void类型指针的含义是,这是一个指针变量,该指针指向一个void类型的数,就是说这个数可能是int, float等等,但是我当前不知道是哪种类型。
void类型指针的作用是,程序不是道是哪种类型,但是程序员是知道的(当时赋值的时候是什么类型,现在就还是什么类型)
- if 选择结构
#include <stdio.h>
int main(void)
{
int a, b, max;
a = 33;
b = 22;
if (a > b){
max = a;
}
else if (a < b){
max = b;
}
else {
max = 0;
}
printf("max = %d.\n", max);
return 0;
}
- switch case 选择结构
int main(void)
{
int num = 2;
switch (num){
case 1:
printf("1\n");
break;
case 2:
printf("2\n");
break;
default:
printf("default\n");
break;
}
}
注意:
case 中必须是常数,且必须是整形(char 也可以被认为是整形)
每个case后面都要有一个break
- for 循环结构
#include <stdio.h>
int main(void)
{
int sum;
sum = 0;
for (int a = 1; a <= 10; a++){
sum += a;
}
printf("sum=%d.\n", sum);
return 0;
}
- while 循环结构
int main(void)
{
int i = 1;
int sum = 0;
while (i <= 100){
sum += i;
i++;
}
printf("sum = %d.\n", sum);
return 0;
}
- do while 循环结构
int main(void)
{
int i = 101;
int sum = 0;
do {
sum += i;
i++;
} while (i <= 100);
printf("sum = %d.\n", sum);
return 0;
}
- 函数
#include <stdio.h>
// 函数声明,全称函数原型声明
int add(int a, int b);
int main(void)
{
int a, b, c;
a = 1;
b = 2;
c = add(a, b); // 函数调用
printf("sum=%d.\n", c);
}
// 函数定义
int add(int a, int b){
return a + b;
}
- 复合数据类型 – 数组
#include <stdio.h>
int main(void)
{
int a[4]; // 定义数组
a[0] = 0;
a[1] = 1;
a[2] = 2;
int b[3] = {0, 1}; // 不完全初始化
b[2] = 2; // 赋值
int c[3] = {}; // 不完全初始化,好处是会把元素默认初始化为0
int g[] = {1, 2, 3}; // 编译器会自动补充初始化的长度
int d[100] = {[50] = 50}; // 将第51个元素初始化
printf("a = %d.\n", b[2]);
char f[4] = {'a', 'b', 'c', 'd'}; // 字符数组
char g[] = "abcde"; // 字符串方式定义字符数组, 默认包含字符串的结尾符 ‘\0’
return 0;
}
- 指针
#include <stdio.h>
int main(void)
{
int a = 23;
int *p; // 定义指针变量p, 该指针指向一个整形数.
p = &a; // &是取地址符,将变量a的地址值赋值给p
*p = 111;
/*
a 代表a变量本身
p 代表指针变量p本身
&a 代表变量a的地址值
*p 代表指针变量p指向的那个变量,也就是变量a
* 指针符号,在指针定义的时候(int *p),*的含义是告诉编译器p是一个指针。在指针操作的时候。*p代表指针变量p指向的那个变量
*/
printf("a = %d.\n", a);
printf("a = %p.\n", p);
return 0;
}
- 指针与数组初步结合
#include <stdio.h>
int main(void)
{
int a[5] = {0, 1, 2, 3, 4};
int *p;
p = &a; // 记录 数组 开始的地址,但是这样会出现警告,因为p是int类型,a 是array类型
p = &a[0]; // 正确,记录了 数组首元素 的地址
p = a; // 看着不对,但其实可以。因为数组变量做右值时,表示的是 数组首元素 的首地址
a[0]; // 访问数组a的第一个元素
a[1]; // 访问数组a的第二个元素
*p; // 访问数组a的第一个元素
*(p + 1); // 访问数组a的第一个元素
printf("a = %d.\n", *(p + 1));
return 0;
}
- 指针的 ++ 与 –
#include <stdio.h>
int main(void)
{
int a[3] = {0, 1, 2};
int *p;
p = a;
p++; // p 指向的地址自增1
printf("a = %d.\n", *p);
printf("a = %d.\n", *p++); // ++ 和 * 的运算优先级一致,运算是从右到左的,但是因为++在变量右边的时候会在运算完成后再自增,所以这里打印出的值是1
return 0;
}
- 指针与传参
传递实参时,实际上是将实参的拷贝传给函数,若想让实参本身参与运算,需要传指针
#include <stdio.h>
int swap(int a, int b);
int swap_pointer(int *p1, int *p2);
int main(void)
{
int a = 1;
int b = 2;
swap(a, b); // 传的是a和b的拷贝
printf("a = %d.\n", a);
printf("b = %d.\n", b);
////////////////////////////////////////////
swap_pointer(&a, &b); // 传的是a和b的地址
printf("a = %d.\n", a);
printf("b = %d.\n", b);
return 0;
}
// 交换两个数
int swap(int a, int b){
int temp;
temp = a;
a = b;
b = temp;
return 0;
}
int swap_pointer(int *p1, int *p2){
int temp;
temp = *p1;
*p1 = *p2;
*p2 = temp;
return 0;
}
- 结构体 – 用来封装几个不同类型的变量
#include <stdio.h>
// 结构体定义的是一种新的组合数据类型
struct person{
char name[20]; // char类型的数组
int age;
int sex;
}; // 这里要有分号
int main(void)
{
struct person per1; // 定义一个结构体变量
per1.name[0] = 'a'; // 结构体赋值
per1.name[1] = 'b';
per1.name[2] = '\0'; // 字符串结束标志
per1.age = 12;
per1.sex = 0;
printf("per1.name = %s.\n", per1.name);
struct person per2 = { // 定义时初始化
"abc",
1,
0
};
printf("per2.name = %s.\n", per2.name);
return 0;
}
- 共用体(联合体, union传)
#include <stdio.h>
union MyUnion{ // 定义共用体,共用体中只有一个对象,这个对象有不同的数据类型的访问形式。通常用在,只需要使用对象中的一个方法,但使用哪个方法不确定。
int a;
char b;
float c;
};
int main(void)
{
union MyUnion u1;
// a 和 b 是不同的数据类型,重合的部分是 -128 ~ 127,所以在这个范围内a和b的值是一致的
u1.a = 1111;
printf("u1.b = %d.\n", u1.b);
return 0;
}
- 宏定义
#include <stdio.h>
#define N1 11111 // 宏定义, 用N 代替 11111, 类似于全局变量吧?
#define N2 1 + 2
#define N3 (265 * 24 * 60 * 60)UL // 需要类型转换,默认会当做int类型处理
int main(void)
{
int a;
a = N1;
printf("a = %d.\n", a);
return 0;
}
- 枚举
#include <stdio.h>
enum week{
SUN,
MON,
TUS,
WEN,
THU,
FRI,
SAT
};
int main(void)
{
enum week today; // 定义一个枚举变量
today = MON; // 不能将 today 赋值为比如HUI这种枚举元素之外的值
switch (today){
case MON:
printf("MON.\n");
break;
case WEN:
printf("WEN.\n");
break;
default:
printf("else.\n");
break;
}
return 0;
}
- 数据类型转换
int main(void)
{
int a = 3;
float b = 3.5;
float c = a + b; // 隐式转换, C语言默认就会进行,默认向精度更高的方向转换。这里编译器会构造一个临时的float变量,将a的值转成float放进去,a本身不变
printf("c = %f.\n", c);
float d;
printf("d = %lu.\n", sizeof((char) d)); // 强制类型转换
return 0;
}
- 静态变量
#include <stdio.h>
void test(void);
int main(void)
{
test();
test();
test();
return 0;
}
void test(void){
static int a = 1; // 静态局部变量,只在第一次调用时才定义和初始化,以后多次调用时,变量的值是上一次执行后的值
a++;
printf("a = %d.\n", a);
}
- 寄存器变量
#include <stdio.h>
void test(void);
int main(void)
{
test();
test();
test();
return 0;
}
void test(void){
register int a = 1; // 寄存器局部变量,直接把变量放到寄存器而不是内存里,这样访问速度就会加快
a++;
printf("a = %d.\n", a);
}