linux下的
是一种通用的、面向过程式的计算机编程语言
#include <stdio.h> //#include 预处理命令,用来引用头文件, stdio.h 头文件
int main() //开始
{
/* 一个注释 */
printf("Hello, World! \n");
return 0; //返回
}
- 安装到vm中
-
链接Xshell
-
安装gcc
-
测试
-
运行源码文件在linux中运行
$ gcc test1.c test2.c -o main.out //多个 $ ./main.out $ gcc hello.c //单个.c文件
基本语法
-
C的令牌(Token) 关键字、标识符、常量、字符串值,或者是一个符号
-
分号结尾
-
注释 // /* */
-
标识符
-
关键字
数据类型
基本类型
- 整数类型
- 浮点(1字节=8bit)
枚举类型
算术类型,定义在程序中只能赋予一定的离散整数值的变量
void类型
没有可用的值。
- 函数返回为空 void exit (int status);
- 函数参数为空 int rand(void);
- 指针指向void void *malloc( size_t size )
派生类型
指针、数组、结构、共用体、函数
变量
定义 type variable ; type variable=value;
变量声明
- int a 声明,也是定义 需要建立存储空间
- extern int a 声明,不是定义 不需要建立存储空间,通过extern声明变量名而不定义它
左值/右值
- 左值lvalue :可以出现在赋值号 左边或右边 int a=0;
- 右值rvalue :术语右值,存储在内存中某些地址的数值。只能出现在右边 10=20(报错)
常量
0 八进制
0x 十六进制
后缀U 无符号
后缀L 长整数
浮点常量 整数部分、小数点、小数部分和指数部分组成:使用小数形式表示时,必须包含整数部分、小数部分,或同时包含两者。当使用指数形式表示时, 必须包含小数点、指数,或同时包含两者。带符号的指数是用 e 或 E 引入的
定义常量
- #definde 预处理器 #define LENGTH 10 //之后LENGTH的值为定值10
- const int var = 5; //不可以缺项之后LENGTH的值在函数内为定值10
存储类
- auto 默认存储类 修饰局部变量
- register 存储类 在寄存器中不是在RAM的局部变量,(一个寄存器大小)
- static 存储类
- 在函数调用之间保持局部变量的值,每次调用该函数时,只初始化一次
- 用于全局变量,作用域在声明它的文件内
static int a=10;//a是全局变量可直接调用
int main(){
while(a--){
fun();}
return 0;
}
void fun(void){
static int b=5;//局部变量,值初始化一次为5,后面不会再重置
b++;
print("%d,%d",a,b);//%输出""内的内容,
}
9,6
8,7
7,8
.....
- extern 存储类 全局变量,对所有程序文件都可见,
- 对于无法初始化的变量,会把变量名指向一个之前定义过的存储位置。
- extern 是用来在另一个文件中声明一个全局变量或函数。
输出 count is 5
运算符
-
逻辑运算符[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E10AOFNF-1676199814932)(D:\1ar\c笔记\逻辑运算符.png)]
-
位运算符
- 杂项
判断
-
if else
-
switch
switch(expression){ case constant-expression : statement(s); break; /* 可选的 */ case constant-expression : statement(s); break; /* 可选的 */ /* 您可以有任意数量的 case 语句 */ default : /* 可选的 */ statement(s); }
-
A ? B : C //A真为B A假为C
循环
- while
- for
- do while
- break //跳出循环
- continue //告诉一个循环体立刻停止本次循环迭代,重新开始下次循环迭代。
- goto //将控制转移到被标记的语句。 少用
函数
- 必有一个main()函数
- 定义一个函数 return_type function_name( parameter list );
- 例:int max(int num1, int num2);
作用域
- 在函数或块内部的局部变量 局部和全局同名时用局部保存在栈
- 在所有函数外部的全局变量 保存在静态存储单元
- 在形式参数的函数参数定义中
数组
type arrayName [ arraySize ];// int a[10];//10个int型数字
- 赋值 类似java
enum(枚举)
enum 枚举名 {枚举元素1,枚举元素2,……};//
-
enum DAY { MON=1, TUE, WED, THU, FRI, SAT, SUN }; //第一个默认为整型0,但此赋值为1,后面依次加一 即 TUE=2...
定义方式
可以与for一起用
- 先定义枚举类型,再定义枚举变量
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
};//可以在函数内也可以在外
enum DAY day;//在函数内
- 定义枚举类型同时定义枚举变量
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
- 省略枚举名称,直接定义枚举变量
enum
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
} day;
-
转换方式
enum day weekend; weekend = ( enum day ) a; //类型转换
指针
type *var_name;
int *p;//定义指针变量
定义一个变量 var=10(会分配一个地址)
定义一个指针 p
指针访问var的地址,于是p指向了变量的地址(&访问地址符号)
- 指针常用操作
- 定义一个指针变量
- 把变量地址赋值给指针
- 访问指针变量中可用地址的值
int main ()
{
int var = 20; /* 实际变量的声明 */
int *ip; /* 指针变量的声明 */
ip = &var; /* 在指针变量中存储 var 的地址 */
printf("var 变量的地址: %p\n", &var );//var的分配地址
/* 在指针变量中存储的地址 */
printf("ip 变量存储的地址: %p\n", ip );//var的分配地址
/* 使用指针访问值 */
printf("*ip 变量的值: %d\n", *ip );//var的值 20
return 0;
}
- 在变量声明时,没有确切的地址,可以给指针赋值null
- 菜鸟教程
概念 | 描述 |
---|---|
指针的算术运算 | 可以对指针进行四种算术运算:++、–、+、- |
指针数组 | 可以定义用来存储指针的数组。 |
指向指针的指针 | C 允许指向指针的指针。 |
传递指针给函数 | 通过引用或地址传递参数,使传递的参数在调用函数中被改变。 |
从函数返回指针 | C 允许函数返回指针到局部变量、静态变量和动态内存分配。 |
指针的算术运算
#include <stdio.h>
const int MAX = 3;
int main ()
{
int var[] = {10, 100, 200};
int i, *ptr;
/* 指针中的数组地址 */
ptr = var;
for ( i = 0; i < MAX; i++)
{
printf("存储地址:var[%d] = %p\n", i, ptr );
printf("存储值:var[%d] = %d\n", i, *ptr );
/* 指向下一个位置 */
ptr++;
}
return 0;
}
存储地址:var[0] = e4a298cc
存储值:var[0] = 10
存储地址:var[1] = e4a298d0 //int 4位所以+4
存储值:var[1] = 100
存储地址:var[2] = e4a298d4
存储值:var[2] = 200
指针数组
int var[] = {10, 100, 200};
int *ptr[MAX];
ptr[i] = &var[i]; /* 赋值为整数的地址 */
printf("var[%d] = %d\n", i, *ptr[i] );//*ptr[i]==*&var[i]==var[i]
var[0]=10
var[1]=100
var[2]=200
指向指针的指针
int **var;
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QQ1jmkKk-1676199814935)(D:\1ar\c笔记\指向指针的指针1.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pMdqMDFU-1676199814936)(D:\1ar\c笔记\指向指针的指针2.png)]
#include <stdio.h>
int main ()
{
int V;
int *Pt1;
int **Pt2;
V = 100;
/* 获取 V 的地址 */
Pt1 = &V;
/* 使用运算符 & 获取 Pt1 的地址 */
Pt2 = &Pt1;
/* 使用 pptr 获取值 */
printf("var = %d\n", V ); //V的值
printf("Pt1 = %p\n", Pt1 ); //V的地址
printf("*Pt1 = %d\n", *Pt1 ); //pt1指针指向的值
printf("Pt2 = %p\n", Pt2 ); //指针pt1的地址
printf("**Pt2 = %d\n", **Pt2); //pt2指向pt1的指针 pt1指针指向的值
return 0;
}
var = 100
Pt1 = 0x7ffee2d5e8d8
*Pt1 = 100
Pt2 = 0x7ffee2d5e8d0 //一个int型值4位 + 一个int型指针 4位
**Pt2 = 100
传递指针给函数
C 传递指针给函数 | 菜鸟教程 (runoob.com)
从函数返回指针
C 从函数返回指针 | 菜鸟教程 (runoob.com)
函数指针
typedef int (*fun_ptr)(int,int); // 声明一个指向同样参数、返回值的函数指针类型
一个指向函数的指针
#include <stdio.h>
int max(int x, int y)
{
return x > y ? x : y;
}
int main(void)
{
/* p 是函数指针 */
int (* p)(int, int) = & max; // &可以省略 p指向max这个函数存储的位置
int a, b, c, d;
printf("请输入三个数字:");
scanf("%d %d %d", & a, & b, & c);
d = p(p(a, b), c); // /* 与直接调用函数等价,d = max(max(a, b), c) */
printf("最大的数字是: %d\n", d);
return 0;
}
回调函数
函数指针变量可以作为某个函数的参数来使用的,回调函数就是一个通过函数指针调用的函数。
简单讲:回调函数是由别人的函数执行时调用你实现的函数。
#include <stdlib.h>
#include <stdio.h>
void populate_array(int *array, size_t arraySize, int (*getNextValue)(void))
{
for (size_t i=0; i<arraySize; i++)
array[i] = getNextValue();
}
// 获取随机值
int getNextRandomValue(void)
{
return rand();
}
int main(void)
{
int myarray[10];
/* getNextRandomValue 不能加括号,否则无法编译,因为加上括号之后相当于传入此参数时传入了 int , 而不是函数指针*/
populate_array(myarray, 10, getNextRandomValue);//此处getNextRandomValue作为int (*getNextValue)(void)形式的参数
for(int i = 0; i < 10; i++) {
printf("%d ", myarray[i]);
}
printf("\n");
return 0;
}
字符串
在 C 语言中,字符串实际上是使用空字符 \0 结尾的一维字符数组。因此,\0 是用于标记字符串的结束。
结构体
一种用户自定义的可用的数据类型
C 结构体 | 菜鸟教程 (runoob.com)
共用体
在相同的内存位置存储不同的数据类型。以定义一个带有多成员的共用体,但是任何时候只能有一个成员带有值。共用体提供了一种使用相同的内存位置的有效方式。
//union结构体
union Data
{
int i;
float f;
char str[20];
};//所占空间大小为,类型中占位最大的
调用
union Date data;
data.i=20;
//如果同时调用data.f ,data.i会损坏
位域
struct packed_struct {
unsigned int f1:1;//4位现只需要1位
unsigned int :1;//空域,占位,该1位不能使用
unsigned int f3:1;//从下一个单元开始存放
unsigned int f4:1;
unsigned int type:4;
unsigned int my_int:9;
} pack;
调用
位域变量名.位域名 // 给位域赋值(应注意赋值不能超过该位域的允许范围)
位域变量名->位域名
pbit=&bit; /* 把位域变量 bit 的地址送给指针变量 pbit */
pbit->a=0; /* 用指针方式给位域 a 重新赋值,赋为 0 */
pbit->b&=3; /* 使用了复合的位运算符 "&=",相当于:pbit->b=pbit->b&3,位域 b 中原有值为 7,与 3 作按位与运算的结果为 3(111&011=011,十进制值为 3) */
pbit->c|=1; /* 使用了复合位运算符"|=",相当于:pbit->c=pbit->c|1,其结果为 15 */
typedef
用来为类型取一个新名字
typedef struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} Book;
/*类型位Book*/
对比typedef #define
- #define是C指令,为个数据类型定义别名
- #define不仅可以为类型定义别名,也能为数值定义别名,typedef只能为类型定义符号名称
- typedef由编译器执行解释,#define由预编译器处理
i/o
C 语言中的 I/O (输入/输出) 通常使用 printf() 和 scanf() 两个函数。
%d整型 **f%**浮点型
getchar() putchar()
-
int getchar(void) 函数从屏幕读取下一个可用的字符,并把它返回为一个整数。这个函数在同一个时间内只会读取一个单一的字符。可以在循环内使用这个方法,从屏幕上读取多个字符。
-
int putchar(int c) 函数把字符输出到屏幕上,并返回相同的字符。这个函数在同一个时间内只会输出一个单一的字符。可以在循环内使用这个方法,以便在屏幕上输出多个字符。
-
#include <stdio.h> int main( ) { char str[100]; printf( "Enter a value :"); gets( str ); printf( "\nYou entered: "); puts( str ); return 0; }
scanf() print()
-
int scanf(const char *format, …) 函数从标准输入流 stdin 读取输入,并根据提供的 format 来浏览输入。
-
int printf(const char *format, …) 函数把输出写入到标准输出流 stdout ,并根据提供的格式产生输出。
-
format 可以是一个简单的常量字符串,但是您可以分别指定 %s、%d、%c、%f 等来输出或读取字符串、整数、字符或浮点数。
#include <stdio.h>
int main( ) {
char str[100];
int i;
printf( "Enter a value :");
scanf("%s %d", str, &i);
printf( "\nYou entered: %s %d ", str, i);
printf("\n");
return 0;
}
32765 我不知道是不是5个backspce的原因
文件读写
/*在Linux系统中*/
#include <stdio.h>
int main()
{
FILE *fp = NULL;
fp = fopen("/tmp/test.txt", "w+");
fprintf(fp, "This is testing for fprintf...\n");
fputs("This is testing for fputs...\n", fp);
fclose(fp);
}
预处理器
C 预处理器只不过是一个文本替换工具而已,它们会指示编译器在实际编译之前完成所需的预处理。(cpp)
参数化的宏
可以使用参数化的宏来模拟函数
int square(int x) {
return x * x;
}
/*可用#define square(x) ((x) * (x))写上面的代码*/
#include <stdio.h>
#define MAX(x,y) ((x) > (y) ? (x) : (y))
int main(void)
{
printf("Max between 20 and 10 is %d\n", MAX(10, 20));
return 0;
}
头文件.h
包含了 C 函数声明和宏定义,被多个源文件中引用共享。有两种类型的头文件:程序员编写的头文件和编译器自带的头文件。需要使用 C 预处理指令 #include 来引用它。
int x;
#include "header.h"/*引用语法时要引用头文件*/
int main (void)
{
puts (test ());//引用头文件中的操作
}
/*从多个不同的头文件中选择一个引用到程序中if elif*/
#if SYSTEM_1
# include "system_1.h"
#elif SYSTEM_2
# include "system_2.h"
#elif SYSTEM_3
...
#endif
强制类型转换
(type_name) expression
强制类型转换运算符的优先级大于除法,
错误处理
errno 、perror()、strerror()
- perror() 函数显示您传给它的字符串,后跟一个冒号、一个空格和当前 errno 值的文本表示形式。
- strerror() 函数,返回一个指针,指针指向当前 errno 值的文本表示形式。
递归(参考数据结构)
可变参数
int func(int, ... )
{
.
.
.
}
int main()
{
func(2, 2, 3);
func(3, 2, 3, 4);
}
请注意,函数 func() 最后一个参数写成省略号,即三个点号(…),省略号之前的那个参数是 int,代表了要传递的可变参数的总数。为了使用这个功能,您需要使用 stdarg.h 头文件,该文件提供了实现可变参数功能的函数和宏。具体步骤如下:
- 定义一个函数,最后一个参数为省略号,省略号前面可以设置自定义参数。
- 在函数定义中创建一个 va_list 类型变量,该类型是在 stdarg.h 头文件中定义的。
- 使用 int 参数和 va_start 宏来初始化 va_list 变量为一个参数列表。宏 va_start 是在 stdarg.h 头文件中定义的。
- 使用 va_arg 宏和 va_list 变量来访问参数列表中的每个项。
- 使用宏 va_end 来清理赋予 va_list 变量的内存。
内存管理
动态分配内存
定义一个指针,指向未定义所需内存大小的字符,再根据需求来分配内存
description = (char *)malloc( 200 * sizeof(char) );
/*通过调用函数 realloc() 来增加或减少已分配的内存块的大小*/
/* 使用 free() 函数释放内存 */
free(description);
命令行参数
都可
int main( int argc, char *argv[] )
int main( int test_argc, char *test_argv[] )
排序算法(数据结构)
冒泡
#include <stdio.h>
void bubble_sort(int arr[], int len) {
int i, j, temp;
for (i = 0; i < len - 1; i++)
for (j = 0; j < len - 1 - i; j++)
if (arr[j] > arr[j + 1]) {
temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
int main() {
int arr[] = { 22, 34, 3, 32, 82, 55, 89, 50, 37, 5, 64, 35, 9, 70 };
int len = (int) sizeof(arr) / sizeof(*arr);
bubble_sort(arr, len);
int i;
for (i = 0; i < len; i++)
printf("%d ", arr[i]);
return 0;
}
选择排序
void selection_sort(int a[], int len)
{
int i,j,temp;
for (i = 0 ; i < len - 1 ; i++)
{
int min = i; // 记录最小值,第一个元素默认最小
for (j = i + 1; j < len; j++) // 访问未排序的元素
{
if (a[j] < a[min]) // 找到目前最小值
{
min = j; // 记录最小值
}
}
if(min != i)
{
temp=a[min]; // 交换两个变量
a[min]=a[i];
a[i]=temp;
}
/* swap(&a[min], &a[i]); */ // 使用自定义函数交換
}
}
/*
void swap(int *a,int *b) // 交换两个变量
{
int temp = *a;
*a = *b;
*b = temp;
}
*/
插入排序
void insertion_sort(int arr[], int len){
int i,j,temp;
for (i=1;i<len;i++){
temp = arr[i];
for (j=i;j>0 && arr[j-1]>temp;j--)
arr[j] = arr[j-1];
arr[j] = temp;
}
}
希尔排序
void shell_sort(int arr[], int len) {
int gap, i, j;
int temp;
for (gap = len >> 1; gap > 0; gap = gap >> 1)
for (i = gap; i < len; i++) {
temp = arr[i];
for (j = i - gap; j >= 0 && arr[j] > temp; j -= gap)
arr[j + gap] = arr[j];
arr[j + gap] = temp;
}
}
归并排序
- 迭代法
int min(int x, int y) {
return x < y ? x : y;
}
void merge_sort(int arr[], int len) {
int* a = arr;
int* b = (int*) malloc(len * sizeof(int));
int seg, start;
for (seg = 1; seg < len; seg += seg) {
for (start = 0; start < len; start += seg + seg) {
int low = start, mid = min(start + seg, len), high = min(start + seg + seg, len);
int k = low;
int start1 = low, end1 = mid;
int start2 = mid, end2 = high;
while (start1 < end1 && start2 < end2)
b[k++] = a[start1] < a[start2] ? a[start1++] : a[start2++];
while (start1 < end1)
b[k++] = a[start1++];
while (start2 < end2)
b[k++] = a[start2++];
}
int* temp = a;
a = b;
b = temp;
}
if (a != arr) {
int i;
for (i = 0; i < len; i++)
b[i] = a[i];
b = a;
}
free(b);
}
- 递归法
void merge_sort_recursive(int arr[], int reg[], int start, int end) {
if (start >= end)
return;
int len = end - start, mid = (len >> 1) + start;
int start1 = start, end1 = mid;
int start2 = mid + 1, end2 = end;
merge_sort_recursive(arr, reg, start1, end1);
merge_sort_recursive(arr, reg, start2, end2);
int k = start;
while (start1 <= end1 && start2 <= end2)
reg[k++] = arr[start1] < arr[start2] ? arr[start1++] : arr[start2++];
while (start1 <= end1)
reg[k++] = arr[start1++];
while (start2 <= end2)
reg[k++] = arr[start2++];
for (k = start; k <= end; k++)
arr[k] = reg[k];
}
void merge_sort(int arr[], const int len) {
int reg[len];
merge_sort_recursive(arr, reg, 0, len - 1);
}
快速排序
- 迭代法
typedef struct _Range {
int start, end;
} Range;
Range new_Range(int s, int e) {
Range r;
r.start = s;
r.end = e;
return r;
}
void swap(int *x, int *y) {
int t = *x;
*x = *y;
*y = t;
}
void quick_sort(int arr[], const int len) {
if (len <= 0)
return; // 避免len等於負值時引發段錯誤(Segment Fault)
// r[]模擬列表,p為數量,r[p++]為push,r[--p]為pop且取得元素
Range r[len];
int p = 0;
r[p++] = new_Range(0, len - 1);
while (p) {
Range range = r[--p];
if (range.start >= range.end)
continue;
int mid = arr[(range.start + range.end) / 2]; // 選取中間點為基準點
int left = range.start, right = range.end;
do
{
while (arr[left] < mid) ++left; // 檢測基準點左側是否符合要求
while (arr[right] > mid) --right; //檢測基準點右側是否符合要求
if (left <= right)
{
swap(&arr[left],&arr[right]);
left++;right--; // 移動指針以繼續
}
} while (left <= right);
if (range.start < right) r[p++] = new_Range(range.start, right);
if (range.end > left) r[p++] = new_Range(left, range.end);
}
}
- 递归法
void swap(int *x, int *y) {
int t = *x;
*x = *y;
*y = t;
}
void quick_sort_recursive(int arr[], int start, int end) {
if (start >= end)
return;
int mid = arr[end];
int left = start, right = end - 1;
while (left < right) {
while (arr[left] < mid && left < right)
left++;
while (arr[right] >= mid && left < right)
right--;
swap(&arr[left], &arr[right]);
}
if (arr[left] >= arr[end])
swap(&arr[left], &arr[end]);
else
left++;
if (left)
quick_sort_recursive(arr, start, left - 1);
quick_sort_recursive(arr, left + 1, end);
}
void quick_sort(int arr[], int len) {
quick_sort_recursive(arr, 0, len - 1);
}
C 语言实例 | 菜鸟教程 (runoob.com)
C 语言经典100例 | 菜鸟教程 (runoob.com)