文章目录
入门C语言基础 1 基础语法 1.1 整形 1.2 浮点型 1.3 常量 1.4 运算符 1.4.1 算数运算符 1.4.2 关系运算 1.4.3 逻辑运算 1.4.4 赋值运算符 1.4.5 其他
1.5 if判断 1.6 循环 1.7 函数 1.8 字符串和字符串 1.9 数组
2 指针 2.1 定义指针类型变量和取变量地址 2.2 指针类型变量解引用 2.3 修改变量的值- 通过指针修改变量值 2.4 指针零值和长度 2.6 指针类型参数 2.7 指针运算(数组的指针) 2.8 指针的指针 2.9 字符串案例 2.9.1 字符串格式 2.9.2 判断字符串包含关系 2.9.3 字符串相加-复制字符串
3 结构体 3.1 结构体基本使用 3.2 单向链表 3.2 双向链表 3.3 双向循环链表
4 预处理和头文件 4.1 预处理 4.2 头文件 4.2.1 main.c 4.2.2 utils.c 4.2.3 utils.h
4.3 Python源码中头文件的使用
入门C语言基础
1 基础语法
1.1 整形
类型 存储大小 值范围 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
# include <stdio.h>
int main ( ) {
char i = 127 ;
printf ( "%d\n" , i) ;
unsigned char i1= 255 ;
printf ( "%d\n" , i1) ;
short i2= 233 ;
printf ( "%d\n" , i2) ;
long i3= 123456 ;
printf ( "%d\n" , i3) ;
int i4= 1234567 ;
printf ( "%d\n" , i4) ;
printf ( "%d\n" , sizeof ( i4) ) ;
printf ( "%d\n" , sizeof ( int ) ) ;
return 0 ;
}
1.2 浮点型
类型 存储大小 值范围 精度 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 位有效位
# include <stdio.h>
int main ( ) {
printf ( "%d\n" , sizeof ( float ) ) ;
printf ( "%d\n" , sizeof ( double ) ) ;
printf ( "%d\n" , sizeof ( long double ) ) ;
float f1= 1.234567F ;
double f2= 1.2345678 ;
printf ( "%f\n" , f1) ;
printf ( "%f\n" , f2) ;
return 0 ;
}
1.3 常量
# include <stdio.h>
int main ( ) {
const int MAX_VALUE= 99 ;
printf ( "%d\n" , MAX_VALUE) ;
return 0 ;
}
1.4 运算符
1.4.1 算数运算符
A 的值为 10,变量 B 的值为 20
运算符 描述 实例 + 把两个操作数相加 A + B 将得到 30 - 从第一个操作数中减去第二个操作数 A - B 将得到 -10 * 把两个操作数相乘 A * B 将得到 200 / 分子除以分母 B / A 将得到 2 % 取模运算符,整除后的余数 B % A 将得到 0 ++ 自增运算符,整数值增加 1 A++ 将得到 11 – 自减运算符,整数值减少 1 A-- 将得到 9
# include <stdio.h>
int main ( ) {
int a= 10 ;
int b= 20 ;
int c;
c= a+ b;
printf ( "a+b的结果是:%d" , c) ;
c = a - b;
printf ( "a-b结果是 %d\n" , c) ;
c = a * b;
printf ( "a * b 结果是%d\n" , c) ;
c = b / a;
printf ( "b / a的值是 %d\n" , c) ;
c = 10 % 3 ;
printf ( "10 % 3取整除的值是 %d\n" , c) ;
c = a++ ;
c= ++ a;
printf ( "赋值后再加 的值是 %d,a的值为:%d\n" , c, a) ;
c = a-- ;
printf ( "赋值后再减 1的值是 %d,a的值为:%d\n" , c, a) ;
return 0 ;
}
1.4.2 关系运算
运算符 描述 == 检查两个操作数的值是否相等,如果相等则条件为真。 != 检查两个操作数的值是否相等,如果不相等则条件为真。 > 检查左操作数的值是否大于右操作数的值,如果是则条件为真。 < 检查左操作数的值是否小于右操作数的值,如果是则条件为真。 >= 检查左操作数的值是否大于或等于右操作数的值,如果是则条件为真。 <= 检查左操作数的值是否小于或等于右操作数的值,如果是则条件为真。
1.4.3 逻辑运算
运算符 描述 && 称为逻辑与运算符。如果两个操作数都非零,则条件为真。 || 称为逻辑或运算符。如果两个操作数中有任意一个非零,则条件为真。 ! 称为逻辑非运算符。用来逆转操作数的逻辑状态。如果条件为真则逻辑非运算符将使其为假。
# include <stdio.h>
int main ( ) {
int a = 10 ;
int b = 20 ;
if ( a && b) {
printf ( "条件为真\n" ) ;
}
return 0 ;
}
1.4.4 赋值运算符
运算符 描述 = 简单的赋值运算符,把右边操作数的值赋给左边操作数 += 加且赋值运算符,把右边操作数加上左边操作数的结果赋值给左边操作数 -= 减且赋值运算符,把左边操作数减去右边操作数的结果赋值给左边操作数 *= 乘且赋值运算符,把右边操作数乘以左边操作数的结果赋值给左边操作数 /= 除且赋值运算符,把左边操作数除以右边操作数的结果赋值给左边操作数 %= 求模且赋值运算符,求两个操作数的模赋值给左边操作数 <<= 左移且赋值运算符 >>= 右移且赋值运算符 &= 按位与且赋值运算符
# include <stdio.h>
int main ( ) {
int a = 21 ;
int c= 10 ;
c += a;
printf ( "%d\n" , c) ;
c *= a;
printf ( "%d\n" , c) ;
return 0 ;
}
1.4.5 其他
运算符 描述 实例 sizeof() 返回变量的大小。 sizeof(a) 将返回 4,其中 a 是整数。 & 返回变量的地址。 &a; 将给出变量的实际地址。 * 指向一个变量。 *a; 将指向一个变量。 ? : 条件表达式 如果条件为真 ? 则值为 X : 否则值为 Y
# include <stdio.h>
int main ( ) {
int a = 10 ;
int b = ( a == 1 ) ? 20 : 30 ;
printf ( "b 的值是 %d\n" , b) ;
return 0 ;
}
printf 占位符
占位符 描述 %d或
%i 有符号十进制整数(int) %u 无符号十进制整数(unsigned int) %o 无符号八进制整数 %x或
%X 无符号十六进制整数(%x
使用小写字母,%X
使用大写字母) %c 字符(实际上是一个整数,按 ASCII 值输出)。 %f 浮点数(double) %e
或 %E
浮点数,指数表示法(%e
使用小写字母 e
,%E
使用大写字母 E
) %g或
%G 浮点数,根据数值的大小自动选择 %f
或 %e
(%g
使用小写字母,%G
使用大写字母)。 %a或
%A 浮点数,十六进制表示法(%a
使用小写字母,%A
使用大写字母) %p 指针地址(通常用于 void*
类型 %s 字符串(char 数组或指针) %n 不输出任何内容,但会将已写入的字符数存储在提供的 int*
变量中。
1.5 if判断
# include <stdio.h>
int main ( ) {
int num;
printf ( "请输入一个数字:" ) ;
scanf ( "%d" , & num) ;
if ( num> 90 ) {
printf ( "优秀" ) ;
} else if ( num>= 60 && num<= 90 ) {
printf ( "及格" ) ;
} else {
printf ( "不及格" ) ;
}
return 0 ;
}
1.6 循环
循环类型 描述 while 循环 当给定条件为真时,重复语句或语句组。它会在执行循环主体之前测试条件。 for 循环 多次执行一个语句序列,简化管理循环变量的代码。 do…while 循环 除了它是在循环主体结尾测试条件外,其他与 while 语句类似。
# include <stdio.h>
int main ( ) {
for ( ; ; ) {
printf ( "死循环" ) ;
}
return 0 ;
}
1.7 函数
# include <stdio.h>
# 返回值类型 函数名 ( 参数a , 参数b)
int add ( int a, int b) {
return a+ b;
}
int main ( ) {
int res= add ( 3 , 4 ) ;
printf ( "%d\n" , res) ;
return 0 ;
}
1.8 字符串和字符串
# include <stdio.h>
int main ( ) {
char a= 'B' ;
printf ( "%c\n" , a) ;
char s[ ] = "justin" ;
char s1[ ] = { 'j' , 'u' , 's' , 't' , 'i' , 'n' , '\0' } ;
printf ( "%s\n" , s) ;
printf ( "%s\n" , s1) ;
return 0 ;
}
1.9 数组
# include <stdio.h>
int main ( ) {
char s1[ ] = { 'j' , 's' , 's' , 's' , 's' , 's' , '\0' } ;
int i1[ ] = { 22 , 33 , 44 , 55 } ;
printf ( "%d\n" , i1[ 1 ] ) ;
printf ( "%c\n" , s1[ 1 ] ) ;
printf ( "%d\n" , sizeof ( i1) ) ;
printf ( "%d\n" , sizeof ( i1) / sizeof ( i1[ 0 ] ) ) ;
char c[ ] = { 22 , 33 , 44 , 55 } ;
printf ( "%d\n" , sizeof ( c) ) ;
printf ( "%d\n" , sizeof ( c[ 0 ] ) ) ;
printf ( "%d\n" , sizeof ( c) / sizeof ( c[ 0 ] ) ) ;
return 0 ;
}
2 指针
- 指针是变量:int ,float 一样,是变量
- 指针是存放 内存地址 0x22123
int v1= 666 ;
int * v2= & v1;
2.1 定义指针类型变量和取变量地址
int main ( ) {
int v1= 666 ;
int * v2= & v1;
printf ( "v2的值是:%p\n" , v2) ;
return 0 ;
}
2.2 指针类型变量解引用
int main ( ) {
int v1= 666 ;
int * v2= & v1;
printf ( "v2的值是:%p\n" , v2) ;
printf ( "v2的指向具体的值是:%d\n" , * v2) ;
printf ( "v1值是:%d\n" , v1) ;
return 0 ;
}
2.3 修改变量的值- 通过指针修改变量值
# include <stdio.h>
int main ( ) {
int v1= 666 ;
int * v2= & v1;
v1= 888 ;
printf ( "v1的值是:%d\n" , v1) ;
printf ( "v2指向具体的值是:%d" , * v2) ;
* v2= 999 ;
printf ( "v1的值是:%d\n" , v1) ;
return 0 ;
}
2.4 指针零值和长度
int main ( ) {
int * v2= NULL ;
if ( ! v2) {
printf ( "指针是空的\n" ) ;
}
printf ( "%d\n" , sizeof ( v2) ) ;
return 0 ;
}
下图就是python 变量能够随便更改数据类型的实现。实际上,python中的变量指向的都是同一类型的内存地址,再由内存地址执行不同类型的值
2.6 指针类型参数
# include <stdio.h>
void changeA ( int a) {
a= 999 ;
printf ( "内部的a是:%d\n" , a) ;
}
void changPointA ( int * p) {
* p= 666 ;
printf ( "内部的p指向的值是:%d\n" , * p) ;
}
int main ( ) {
int a = 1 ;
changeA ( a) ;
changPointA ( & a) ;
printf ( "外部的a是:%d\n" , a) ;
return 0 ;
}
2.7 指针运算(数组的指针)
int * p= & [ 1 , 2 , 3 , ] [ * v1, * v2, * v3]
# include <stdio.h>
int main ( ) {
int a[ 3 ] = { 11 , 22 , 33 } ;
int * p= & a;
printf ( "p的值是:%p\n" , p) ;
printf ( "p解引用是:%d\n" , * p) ;
printf ( "数组第二个位置值是:%d\n" , a[ 1 ] ) ;
return 0 ;
}
在C语言中,无论是通过指针还是通过数组下标来访问数组元素,如果访问超出了数组定义的边界(例如,对于定义为a[3]的数组,尝试访问a[4]),这种行为是未定义的(undefined behavior)。尽管在某些情况下,程序可能会在内存中“拿到”一个值(这通常是因为内存布局的原因,使得越界访问没有立即导致程序崩溃),但这并不意味着这个值是有效的或安全的。这种越界访问实际上会导致内存溢出(或称为内存越界),它可能引发各种不可预测的问题,包括数据损坏、程序崩溃或安全漏洞。
2.8 指针的指针
int main ( ) {
int a = 100 ;
int * p1 = & a;
int * * p2 = & p1;
int * * * p3 = & p2;
printf ( "p3的值是:%p\n" , p3) ;
printf ( "p3指向的值是(其实是p2的值):%p\n" , * p3) ;
printf ( "p2的值是:%p\n" , p2) ;
printf ( "通过p3拿到真正的a的值:%d\n" , * * * p3) ;
return 0 ;
}
2.9 字符串案例
2.9.1 字符串格式
# include <stdio.h>
int main ( ) {
char a[ 6 ] ;
char * p = & a;
sprintf ( p, "%c" , 'j' ) ;
p+= 1 ;
sprintf ( p, "%c" , 'u' ) ;
p+= 1 ;
sprintf ( p, "%c" , 's' ) ;
p+= 1 ;
sprintf ( p, "%c" , 't' ) ;
p+= 1 ;
sprintf ( p, "%c" , 'i' ) ;
p+= 1 ;
sprintf ( p, "%c" , 'n' ) ;
printf ( "a的值:%s\n" , a) ;
return 0 ;
}
2.9.2 判断字符串包含关系
# include <stdio.h>
# include <string.h>
int main ( ) {
char name[ ] = "justin is handsome" ;
char * res= strstr ( name, "handsome" ) ;
if ( res) {
printf ( "存在" ) ;
} else {
printf ( "不存在" ) ;
}
return 0 ;
}
2.9.3 字符串相加-复制字符串
# include <stdio.h>
# include <string.h>
# include <stdlib.h>
int main ( ) {
char name[ ] = "justin" ;
char role[ ] = "teacher" ;
char * s= malloc ( strlen ( name) + strlen ( role) + 1 ) ;
strcpy ( s, name) ;
strcat ( s, role) ;
printf ( "%s\n" , s) ;
return 0 ;
}
3 结构体
相当于类,放了一堆属性
3.1 结构体基本使用
# include <stdio.h>
struct Person {
char name[ 30 ] ;
int age;
} ;
int main ( ) {
struct Person p1= { "justin" , 19 } ;
printf ( "人名是:%s,年龄是:%d\n" , p1. name, p1. age) ;
struct Person * p2= & p1;
printf ( "人名是:%s,年龄是:%d\n" , p2-> name, p2-> age) ;
return 0 ;
}
3.2 单向链表
# include <stdio.h>
struct Node {
int data;
struct Node * next;
} ;
int main ( ) {
struct Node v3= { 33 } ;
struct Node v2= { 22 , & v3} ;
struct Node v1= { 11 , & v2} ;
printf ( "v1的数据是:%d\n" , v1. data) ;
printf ( "v1的下一个节点的数据(v2的data):%d\n" , v1. next-> data) ;
printf ( "v1的下一个节点的下一个节点数据(v2的data):%d\n" , v1. next-> next-> data) ;
return 0 ;
}
3.2 双向链表
# include <stdio.h>
struct Node {
int data;
struct Node * prev;
struct Node * next;
} ;
int main ( ) {
struct Node v3= { 33 } ;
struct Node v2= { 22 } ;
struct Node v1= { 11 } ;
v1. next= & v2;
v2. next= & v3;
v2. prev= & v1;
v3. prev= & v2;
printf ( "v1的值是:%d\n" , v1. data) ;
printf ( "通过v1拿到v2的值是:%d\n" , v1. next-> data) ;
printf ( "通过v1拿到v3的值是:%d\n" , v1. next-> next-> data) ;
printf ( "v3的值是:%d\n" , v3. data) ;
printf ( "通过v3拿到v2的值是:%d\n" , v1. prev-> data) ;
printf ( "通过v3拿到v1的值是:%d\n" , v1. prev-> prev-> data) ;
return 0 ;
}
3.3 双向循环链表
# include <stdio.h>
struct Node {
int data;
struct Node * prev;
struct Node * next;
} ;
int main ( ) {
struct Node v3= { 33 } ;
struct Node v2= { 22 } ;
struct Node v1= { 11 } ;
v1. next= & v2;
v1. prev = & v3
v2. next= & v3;
v2. prev= & v1;
v3. prev= & v2;
v3. next= & v1;
printf ( "v1的值是:%d\n" , v1. data) ;
printf ( "通过v1拿到v2的值是:%d\n" , v1. next-> data) ;
printf ( "通过v1拿到v3的值是:%d\n" , v1. next-> next-> next-> next-> data) ;
printf ( "v3的值是:%d\n" , v3. data) ;
printf ( "通过v3拿到v2的值是:%d\n" , v1. prev-> data) ;
printf ( "通过v3拿到v1的值是:%d\n" , v1. prev-> prev-> data) ;
return 0 ;
}
4 预处理和头文件
4.1 预处理
# include <stdio.h>
# define ME 200
# define SIZE 18
int main ( ) {
printf ( "%d\n" , ME) ;
return 0 ;
}
4.2 头文件
项目目录
├── main. c 主文件
├── utils. c 些函数方法
└── utils. h 用来注册能被导出的函数
4.2.1 main.c
#include <stdio.h>
#include "utils.h" // 内置的用 <> ,自定义的用 ""
int main() {
int res=add(4,5);
printf("%d\n",res);
}
4.2.2 utils.c
int add ( int a, int b) {
return a + b + 100 ;
}
int a ( ) {
return 11 ;
}
4.2.3 utils.h
int add ( int a, int b) ;
4.3 Python源码中头文件的使用
https: // www. python. org/ downloads/ source/
https: // github. com/ python/ cpython/ tree/ main/ Objects
https: // github. com/ python/ cpython/ blob/ main/ Objects/ listobject. c
https: // github. com/ python/ cpython/ blob/ main/ Include/ listobject. h