C语言基础学习之基本语法
写在前面
本部分看下枯燥无聊而又不得不看的基础语法。
0:零散知识点
0.1:typedef
typedef用来定义新类型,如:
// 定义一个新的无符号类型COUNT
typedef unsigned int COUNT;
COUNT counter;
// 定义一个新的结构体类型Simple2
typedef struct
{
int a;
char b;
double c;
} Simple2;
//现在可以用Simple2作为类型声明新的结构体变量
Simple2 u1, u2[20], *u3;
0.2:void *
void * 类型表示未确定类型的指针。C、C++ 规定 void * 类型可以通过类型转换强制转换为任何其它类型的指针。
1:宏和const定义常量
宏和const都可以用来定义常量,但是宏没有类型,且作用范围为整个文件,而const定义常量需要类型,且范围为对应的方法或者是代码块:
#include <stdio.h>
// 定义一个宏,无类型,全局使用
#define LENGTH 10
#define WIDTH 5
int main() {
int size = LENGTH * WIDTH;
printf("长: %d", LENGTH);
printf("xxxx: %d", size);
const float PI = 3.14159265758979;
printf("呸是: %f", PI);
return 0;
}
运行:
PS E:\vscode-workspace\C> cd "e:\vscode-workspace\C\" ; if ($?) { gcc includeAndConst.c -o includeAndConst } ; if ($?) { .\includeAndConst }
长: 10xxxx: 50呸是: 3.141593
2:sizeof
用来计算数据某数据类型的占用空间大小:
#include <stdio.h>
#include <float.h>
int main() {
printf("int类型的大小:%d\n", sizeof(int));
printf("float类型的大小:%d\n", sizeof(float));
return 0;
}
int类型的大小:4
float类型的大小:4
3:extern关键字
声明变量是外部变量:
#include <stdio.h>
// 函数外定义变量 x 和 y
int x;
int y;
int addtwonum()
{
// 函数内声明变量 x 和 y 为外部变量
extern int x;
extern int y;
// 给外部变量(全局变量)x 和 y 赋值
x = 1;
y = 2;
return x+y;
}
int main()
{
int result;
// 调用函数 addtwonum
result = addtwonum();
printf("result 为: %d",result);
return 0;
}
result 为: 3
也可以引用另一个文件中的变量,先定义addtwonum.c
文件定义变量:
#include <stdio.h>
/*外部变量声明*/
extern int x ;
extern int y ;
int addtwonum()
{
return x+y;
}
定义文件test.c
引用变量:
#include <stdio.h>
/*定义两个全局变量*/
int x=1;
int y=2;
int addtwonum();
int main(void)
{
int result;
result = addtwonum();
printf("result 为: %d\n",result);
return 0;
}
测试:
PS E:\vscode-workspace\C> gcc .\addtwonum.c .\test.c -o main
PS E:\vscode-workspace\C> ./main
result 为: 3
4:整数常量后缀是 U 和 L
U 表示无符号整数(unsigned),L 表示长整数(long)。后缀可以是大写,也可以是小写,U 和 L 的顺序任意。
212 /* 合法的 */
215u /* 合法的 */
0xFeeL /* 合法的 */
078 /* 非法的:8 不是八进制的数字 */
032UU /* 非法的:不能重复后缀 */
85 /* 十进制 */
0213 /* 八进制 */
0x4b /* 十六进制 */
30 /* 整数 */
30u /* 无符号整数 */
30l /* 长整数 */
30ul /* 无符号长整数 */
int myInt = 10;
long myLong = 100000L;
unsigned int myUnsignedInt = 10U;
5:&
和*
&
获取变量指针,*
通过指针访问变量值:
#include <stdio.h>
int a = 4;
// 定义int类型的指针
int* ptr;
int main(void)
{
int a = 4;
ptr = &a; /* 'ptr' 现在包含 'a' 的地址 */
printf("a 的值是 %d\n", a);
printf("*ptr 是 %d\n", *ptr);
return 0;
}
a 的值是 4
*ptr 是 4
6:通过sizeof和宏获取数组元素个数
6.1:使用sizeof
C语言并没有length之类的方法或者是属性来获取元素的个数,即数组的长度,但是可以通过sizeof来实现,思路是先获取总的内存占用大小,然后除以一个元素的内存大小就得到了元素的个数了:
#include <stdio.h>
int main() {
int array[] = {1, 2, 3, 4, 5};
// sizeof(array) 总占用大小 sizeof(array[0])一个元素占用大小
int length = sizeof(array) / sizeof(array[0]);
printf("数组长度为: %d\n", length);
return 0;
}
运行:
数组长度为: 5
6.2:使用宏
如下:
#include <stdio.h>
#define LENGTH(array) (sizeof(array) / sizeof(array[0]))
int main() {
int array1[] = {1, 2, 3, 4, 5};
// 类似于函数调用,但实际不是,因为是在编译时会进行替换为
// int length = (sizeof(array1) / sizeof(array1[0])) 这样的好处是如果有很多地方都需要获取数组的长度,就可以不需要写大量重复的代码了
int length = LENGTH(array1);
printf("数组长度为: %d\n", length);
return 0;
}
运行:
数组长度为: 5
7:枚举enum
定义一个枚举类型,需要使用 enum 关键字,后面跟着枚举类型的名称,以及用大括号 {} 括起来的一组枚举常量。每个枚举常量可以用一个标识符来表示,也可以为它们指定一个整数值,如果没有指定,那么默认从 0 开始递增。
在C 语言中,枚举类型是被当做 int 或者 unsigned int 类型来处理的。
可以先定义枚举类型,再定义枚举变量,也可以定义枚举类型的同时定义枚举变量。看个例子:
#include <stdio.h>
// 定义枚举
enum DAY
{
MON=1, TUE, WED, THU, FRI, SAT, SUN
} oneDay; // 定义枚举的同时直接生成名一个变量
int main()
{
// 定义DAY枚举类型的变量,并赋值为WED
enum DAY day;
day = WED;
printf("rr %d\n",day);
oneDay = SAT;
printf("nn %d\n",SAT);
return 0;
}
输出:
rr 3
nn 6
7.1:枚举在switch中使用
#include <stdio.h>
#include <stdlib.h>
int main()
{
enum color { red=1, green, blue };
// 定义枚举变量 favorite_color
enum color favorite_color;
/* 用户输入数字来选择颜色 */
printf("请输入你喜欢的颜色: (1. red, 2. green, 3. blue): ");
// 将用户录入的值写到favorite_color的地址,即赋值到favorite_color
scanf("%u", &favorite_color);
/* 输出结果 */
switch (favorite_color)
{
case red:
printf("你喜欢的颜色是红色");
break;
case green:
printf("你喜欢的颜色是绿色");
break;
case blue:
printf("你喜欢的颜色是蓝色");
break;
default:
printf("你没有选择你喜欢的颜色");
}
return 0;
}
输出:
请输入你喜欢的颜色: (1. red, 2. green, 3. blue): 1
你喜欢的颜色是红色
8:指针
想要成为一名优秀的 C 程序员,学习指针是很有必要的。
定义指针:
type *var_name;
在这里,type 是指针的基类型,它必须是一个有效的 C 数据类型,var_name 是指针变量的名称。在这个语句中,星号是用来指定一个变量是指针。以下是有效的指针声明:
int *ip; /* 一个整型的指针 */
double *dp; /* 一个 double 型的指针 */
float *fp; /* 一个浮点型的指针 */
char *ch; /* 一个字符型的指针 */
指针也就是内存地址(一个整数类型的长串,一般使用16进制的形式来表示)
,&
获取变量地址,*
通过变量地址访问变量值,小例子:
#include <stdio.h>
int main ()
{
int var_runoob = 10;
int *p; // 定义int类型的指针变量
// 获取变量地址
p = &var_runoob;
printf("var_runoob 变量的地址: %p\n", p);
printf("通过*运算符获取var_runoob 变量的值: %d\n", *p);
return 0;
}
var_runoob 变量的地址: 000000EF8E5FFC94
通过*运算符获取var_runoob 变量的值: 10
再一个例子:
#include <stdio.h>
int main ()
{
int var = 20; /* 实际变量的声明 */
int *ip; /* 指针变量的声明,int类型 */
ip = &var; /* &运算符获取变量的地址,并赋值到指针变量 */
printf("var 变量的地址: %p\n", &var );
/* 在指针变量中存储的地址 */
printf("ip 变量存储的地址: %p\n", ip );
/* 使用*运算符通过指针访问值 */
printf("*ip 变量的值: %d\n", *ip );
return 0;
}
var 变量的地址: 000000FB01FFF864
ip 变量存储的地址: 000000FB01FFF864
*ip 变量的值: 20
8.1:NULL 指针
在变量声明的时候,如果没有确切的地址可以赋值,为指针变量赋一个 NULL 值是一个良好的编程习惯。赋为 NULL 值的指针被称为空指针。
NULL 指针是一个定义在标准库中的值为零的常量。请看下面的程序:
#include <stdio.h>
int main ()
{
int *ptr = NULL;
printf("ptr 的地址是 %p\n", ptr );
return 0;
}
ptr 的地址是 0000000000000000
在大多数的操作系统上,程序不允许访问地址为 0 的内存,因为该内存是操作系统保留的。然而,内存地址 0 有特别重要的意义,它表明该指针不指向一个可访问的内存位置。但按照惯例,如果指针包含空值(零值),则假定它不指向任何东西。
如需检查一个空指针,您可以使用 if 语句,如下所示:
#include <stdio.h>
int main()
{
int *ptr = NULL;
if (ptr)
{
printf("不是空指针啊!");
} else {
printf("空指针啊");
}
printf("\n");
if (!ptr)
{
printf("使用!判断空指针啊");
}
return 0;
}
空指针啊
使用!判断空指针啊
8.2:指针内容(也比较重要)
8.3:函数指针
如其名称,为指向函数的指针,格式返回值类型 (*指针名称)(参数类型列表);
,如int (*fun_ptr)(int,int);
。
例子:
#include <stdio.h>
int max(int x, int y)
{
return x > y ? x : y;
}
int main(void)
{
/* p 是函数指针,指向函数max */
int (* p)(int, int) = & max; // &可以省略
int a, b, c, d;
printf("请输入三个数字:");
scanf("%d %d %d", & a, & b, & c);
/* 与直接调用函数等价,d = max(max(a, b), c) */
// 使用函数指针p执行两次函数调用
d = p(p(a, b), c);
printf("最大的数字是: %d\n", d);
return 0;
}
结果:
请输入三个数字:6
5
8
最大的数字是: 8
8.3:函数指针与回调函数
回调函数是将函数作为函数的参数,调用者传入,回调执行,例子:
#include <stdlib.h>
#include <stdio.h>
// size_t相当于int getNextValue是函数指针的名称
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 , 而不是函数指针*/
// 使用回调函数getNextRandomValue给数组myarray初始化值
populate_array(myarray, 10, getNextRandomValue);
for(int i = 0; i < 10; i++) {
printf("%d ", myarray[i]);
}
printf("\n");
return 0;
}
执行:
41 18467 6334 26500 19169 15724 11478 29358 26962 24464
9:size_t
在C语言中,size_t 是一个表示大小的无符号整数类型,通常用于数组索引和循环计数器。它是由 typedef 定义的,不是一个新的数据类型,也不是关键字。size_t 的大小足以保证存储内存中对象的大小,其取值范围与机器架构和操作系统相关。在32位系统中通常是 unsigned int,而在64位系统中通常是 unsigned long。
size_t size = 5;
printf("%zu\n", size); // 显示5
10:C 字符串
在 C 语言中,字符串实际上是使用空字符 \0 结尾的一维字符数组。因此,\0 是用于标记字符串的结束。
空字符(Null character)又称结束符,缩写 NUL,是一个数值为 0 的控制字符,\0 是转义字符,意思是告诉编译器,这不是字符 0,而是空字符。 \0
可以省略不写,编译器会默认添加,但长度必须多一个,例子:
#include <stdio.h>
int main ()
{
char site[7] = {'R', 'U', 'N', 'O', 'O', 'B', '\0'};
printf("菜鸟教程: %s\n", site );
// \0 可以省略不写,编译器会默认添加,但长度必须多一个
char site1[3] = {'A', 'B' };
printf("菜鸟教程1: %s\n", site1 );
return 0;
}
结果:
菜鸟教程: RUNOOB
菜鸟教程1: AB
11:常用函数
例子:
#include <stdio.h>
#include <string.h>
int main ()
{
char str1[14] = "runoob";
char str2[14] = "google";
char str3[14];
int len ;
/* 复制 str1 到 str3 */
strcpy(str3, str1);
printf("strcpy( str3, str1) : %s\n", str3 );
/* 连接 str1 和 str2 */
strcat( str1, str2);
printf("strcat( str1, str2): %s\n", str1 );
/* 连接后,str1 的总长度 */
len = strlen(str1);
printf("strlen(str1) : %d\n", len );
return 0;
}
结果:
strcpy( str3, str1) : runoob
strcat( str1, str2): runoobgoogle
strlen(str1) : 12
12:结构体
类似Java的对象,struct 语句定义了一个包含多个成员的新的数据类型,struct 语句的格式如下:
struct tag {
member-list
member-list
member-list
...
} variable-list ;
如:
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} book;
其他类型的定义实例:
//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c
//同时又声明了结构体变量s1
//这个结构体并没有标明其标签
struct
{
int a;
char b;
double c;
} s1;
//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c
//结构体的标签被命名为SIMPLE,没有声明变量
struct SIMPLE
{
int a;
char b;
double c;
};
//用SIMPLE标签的结构体,另外声明了变量t1、t2、t3
// 注意这里要带着struct关键字,如果是是使用typedef声明了新变量类型的话就不需要struct关键字了
struct SIMPLE t1, t2[20], *t3;
//也可以用typedef创建新类型
typedef struct
{
int a;
char b;
double c;
} Simple2;
//现在可以用Simple2作为类型声明新的结构体变量
// 这里使用typedef声明了新类型后就不需要在前面写struct关键字了
Simple2 u1, u2[20], *u3;
12.1:互相包含结构体
互相包含需要进行不完整声明,这样可以先用
:
struct B; //对结构体B进行不完整声明
//结构体A中包含指向结构体B的指针
struct A
{
struct B *partner; // 先使用不完整声明了
//other members;
};
//结构体B中包含指向结构体A的指针,在A声明完后,B也随之进行声明
struct B
{
struct A *partner;
//other members;
};
12.2:结构体变量的初始化
#include <stdio.h>
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
} book = {"C 语言", "RUNOOB", "编程语言", 123456};
int main()
{
printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book.title, book.author, book.subject, book.book_id);
}
运行:
title : C 语言
author: RUNOOB
subject: 编程语言
book_id: 123456
12.3:访问结构成员
#include <stdio.h>
#include <string.h>
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
};
int main( )
{
struct Books Book1; /* 声明 Book1,类型为 Books */
struct Books Book2; /* 声明 Book2,类型为 Books */
/* Book1 详述 */
strcpy( Book1.title, "C Programming");
strcpy( Book1.author, "Nuha Ali");
strcpy( Book1.subject, "C Programming Tutorial");
Book1.book_id = 6495407;
/* Book2 详述 */
strcpy( Book2.title, "Telecom Billing");
strcpy( Book2.author, "Zara Ali");
strcpy( Book2.subject, "Telecom Billing Tutorial");
Book2.book_id = 6495700;
/* 输出 Book1 信息 */
printf( "Book 1 title : %s\n", Book1.title);
printf( "Book 1 author : %s\n", Book1.author);
printf( "Book 1 subject : %s\n", Book1.subject);
printf( "Book 1 book_id : %d\n", Book1.book_id);
/* 输出 Book2 信息 */
printf( "Book 2 title : %s\n", Book2.title);
printf( "Book 2 author : %s\n", Book2.author);
printf( "Book 2 subject : %s\n", Book2.subject);
printf( "Book 2 book_id : %d\n", Book2.book_id);
return 0;
}
运行:
Book 1 title : C Programming
Book 1 author : Nuha Ali
Book 1 subject : C Programming Tutorial
Book 1 book_id : 6495407
Book 2 title : Telecom Billing
Book 2 author : Zara Ali
Book 2 subject : Telecom Billing Tutorial
Book 2 book_id : 6495700
12.4:结构体作为函数参数
#include <stdio.h>
#include <string.h>
// 定义结构体
struct Books
{
char title[50];
char author[50];
char subject[100];
int book_id;
};
/* 函数声明 */
// 注意这里要有struct关键字,如果是使用typedef的话就不需要了
void printBook( struct Books book );
int main( )
{
struct Books Book1; /* 声明 Book1,类型为 Books */
struct Books Book2; /* 声明 Book2,类型为 Books */
/* Book1 详述 */
strcpy( Book1.title, "C Programming");
strcpy( Book1.author, "Nuha Ali");
strcpy( Book1.subject, "C Programming Tutorial");
Book1.book_id = 6495407;
/* Book2 详述 */
strcpy( Book2.title, "Telecom Billing");
strcpy( Book2.author, "Zara Ali");
strcpy( Book2.subject, "Telecom Billing Tutorial");
Book2.book_id = 6495700;
/* 输出 Book1 信息 */
printBook( Book1 );
/* 输出 Book2 信息 */
printBook( Book2 );
return 0;
}
// 注意这里要有struct关键字,如果是使用typedef的话就不需要了
void printBook( struct Books book )
{
printf( "Book title : %s\n", book.title);
printf( "Book author : %s\n", book.author);
printf( "Book subject : %s\n", book.subject);
printf( "Book book_id : %d\n", book.book_id);
}
运行:
Book title : C Programming
Book author : Nuha Ali
Book subject : C Programming Tutorial
Book book_id : 6495407
Book title : Telecom Billing
Book author : Zara Ali
Book subject : Telecom Billing Tutorial
Book book_id : 6495700
12.5:使用sizeof计算结构体大小
#include <stdio.h>
struct Person {
char name[20];
int age;
float height;
};
int main() {
struct Person person;
printf("结构体 Person 大小为: %zu 字节\n", sizeof(person));
return 0;
}
运行:
结构体 Person 大小为: 28 字节
13:联合体
使用关键字union,使用同一块内存来存储多种类型的变量,定义如下:
union [union tag]
{
member definition;
member definition;
...
member definition;
} [one or more union variables];
union Data
{
int i;
float f;
char str[20];
} data;
13.1:占用大小
按照所需的最大类型分配内存,程序:
#include <stdio.h>
#include <string.h>
union Data
{
int i;
float f;
char str[20];
};
int main( )
{
union Data data;
printf( "Memory size occupied by data : %d\n", sizeof(data));
return 0;
}
运行:
Memory size occupied by data : 20
13.2:实战
#include <stdio.h>
#include <string.h>
union Data
{
int i;
float f;
char str[20];
};
int main( )
{
union Data data;
data.i = 10;
data.f = 220.5;
strcpy( data.str, "C Programming");
printf( "data.i : %d\n", data.i);
printf( "data.f : %f\n", data.f);
printf( "data.str : %s\n", data.str);
return 0;
}
运行:
data.i : 1917853763
data.f : 4122360580327794860452759994368.000000
data.str : C Programming
在这里,我们可以看到共用体的 i 和 f 成员的值有损坏,因为最后赋给变量的值占用了内存位置,这也是 str 成员能够完好输出的原因。
14:位域
C 语言的位域(bit-field)是一种特殊的结构体成员,允许我们按位对成员进行定义,指定其占用的位数。可以使用位域的类型只能为 int(整型),unsigned int(无符号整型),signed int(有符号整型) 三种类型。正常如下:
struct
{
unsigned int widthValidated;
unsigned int heightValidated;
} status;
只需要增加: 长度
就可以来定义位域了。如下:
struct
{
unsigned int widthValidated : 1;
unsigned int heightValidated : 1;
} status;
注意此时占用4个字节,因为最少要分配4个字节吧!
例子:
#include <stdio.h>
#include <string.h>
/* 定义简单的结构 */
struct
{
unsigned int widthValidated;
unsigned int heightValidated;
} status1;
/* 定义位域结构 */
struct
{
unsigned int widthValidated : 1;
unsigned int heightValidated : 1;
} status2;
int main( )
{
printf( "Memory size occupied by status1 : %d\n", sizeof(status1));
printf( "Memory size occupied by status2 : %d\n", sizeof(status2));
return 0;
}
运行:
D:\test>gcc -o one abc.c
D:\test>one.exe
Memory size occupied by status1 : 8
Memory size occupied by status2 : 4
15:头文件
头文件是扩展名为 .h 的文件,包含了 C 函数声明和宏定义,被多个源文件中引用共享。有两种类型的头文件:程序员编写的头文件和编译器自带的头文件。如:
#include <stdio.h>
#include <string.h>
15.1:标准库头文件
16:内存管理
内存管理相关函数如下:
函数可以在 <stdlib.h> 头文件中找到。
16.1:C 语言中常用的内存管理函数和运算符
17:命令行参数
执行程序时,可以从命令行传值给 C 程序。这些值被称为命令行参数,它们对程序很重要,特别是当您想从外部控制程序,而不是在代码内对这些值进行硬编码时,就显得尤为重要了。
函数原型:
int main(int argc, char *argv[]);
或者:
int main(int argc, char **argv);
argc (argument count): 表示命令行参数的数量,包括程序名本身。因此,argc 至少为 1。
argv (argument vector): 是一个指向字符串数组的指针,其中每个字符串是一个命令行参数。数组的第一个元素(即 argv[0])通常是程序的名称。接下来的元素是传递给程序的命令行参数。
例子:
#include <stdio.h>
int main( int argc, char *argv[] )
{
if( argc == 2 )
{
printf("The argument supplied is %s\n", argv[1]);
}
else if( argc > 2 )
{
printf("Too many arguments supplied.\n");
}
else
{
printf("One argument expected.\n");
}
}
运行:
D:\test>gcc -o one abc.c
D:\test>one.exe
One argument expected.
D:\test>one.exe aaaaa
The argument supplied is aaaaa
D:\test>one.exe aaaaa bbbbb
Too many arguments supplied.
D:\test>
18:系统头文件
类似工具类+常量类
的组合。
写在后面
参考文章列表
菜鸟教程。