Android 音视频入门:C语言基础
2020/3/31 23:02:23
本文主要是介绍Android 音视频入门:C语言基础,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
概述
C语言对于Android开发来说还是非常必要的,不管你是要阅读源码,还是想要学习NDK,音视频,性能优化等,都不可避免需要接触到C,而且C语言属于系统级的语言,操作系统内核都有C的身影,所以我今天学习一下C语言,本篇博客作为笔记,以防以后忘记
![](/upload/202003/31/202003312302237598.png)
C简介
C语言最初适用于系统开发工作的,特别是组成操作系统的程序,由于C语言产生的代码运行速度与汇编编写的代码运行速度几乎相同,所以采用C语言作为系统开发语言,下面列举几个使用C的实例
- 操作系统
- 文本编辑器
- 打印机
- 网络驱动器等
C环境的设置
写在源文件的中代码,使我们人类可以看懂的,他需要编译转换为机器语言,这样CPU可以按照指令执行程序,而C语言可以通过GUN编译器,把源码编译为机器语言
Mac上直接下载Xcode就可以使用GUN编译器,开发工具可以使用CLion
或者直接用文本编译器
先写一个HelloWord
#include <stdio.h> int main() { /* 我的第一个 C 程序 */ printf("第一个c程序Hello, World! \n"); return 0; } 复制代码
一个C程序包括
- 预处理器指令 : 程序的第一行
#include <stdio.h>
是预处理指令,告诉C编译器实际编译之前需要包括stdio.h
文件 - 函数 : 下一行
int main()
是主函数,程序从这里开始运行,printf()
是程序中另一个可用的函数作用是在屏幕上显示Hello, World!
- 变量
- 语句&表达式
- 注释: /* ... */这就是一个注释
return 0;
标识此函数结束,并返回0
编译执行C程序
- 首先打开文本编辑器添加上方代码
- 保存文件为hello.c
- 打开命令行,进入文件的文件夹
- 命令行输入gcc hello.c,编译代码
- 如果没有错误的话,文件夹就会生成a.out的可执行文件
- 命令行输入a.out,运行程序
- 然后屏幕上就可以看到
Hello, World!
Last login: Mon Feb 17 14:53:00 on ttys002 L-96FCG8WP-1504:~ renxiaohui$ cd Desktop/ L-96FCG8WP-1504:Desktop renxiaohui$ cd c/ L-96FCG8WP-1504:c renxiaohui$ gcc hello.c L-96FCG8WP-1504:c renxiaohui$ a.out 第一个c程序 Hello, World! L-96FCG8WP-1504:c renxiaohui$ 复制代码
C语言入门
1 基本语法
标识符
标记符是用来标记变量,函数或者任何用户自定的变量名称,一个标识符以字母A—Z,a-z,或者_下划线开始,后面跟0个或多个字母,数字,下划线
标识符中不允许出现标点字符,比如@%,标识符是区分大小写的
关键字
这些关键字不能作为常量名或者变量名,其他标识符的名称
关键字 | 说明 |
---|---|
continue | 结束当前循环,开始下一轮循环 |
switch | 用于开关语句 |
case | 开关语句分支 |
default | 开关语句中的其他分支 |
break | 跳出当前循环 |
do | 循环语句的循环体 |
while | 循环语句的循环条件 |
if | 条件语句 |
else | 条件语句否定分支与if一起使用 |
for | 一种循环语句 |
goto | 无条件跳转语句 |
return | 子程序返回语句,可带参数可不带参数 |
char | 声明字符变量或者返回值类型 |
double | 声明双精度浮点类型对象或函数返回值类型 |
float | 声明浮点型变量或返回值类型 |
short | 声明短整形变量或者返回值类型 |
int | 声明整形变量或者返回值类型 |
long | 声明长整形变量或返回值类型 |
unsigned | 声明无符号类型变量或返回值类型 |
void | 声明函数无返回值或无参数,声明无类型指针 |
enum | 声明枚举类型 |
static | 声明静态变量 |
auto | 自动声明变量 |
const | 定义常量,如果一个变量被const修饰,则它的值不能被改变 |
extern | 声明变量或函数在其他文件或本文件的其他位置定义 |
register | 声明寄存器变量 |
signed | 声明有符号类型的变量 |
sizeof | 计算数据类型或变量的长度 |
struct | 声明结构体类型 |
union | 声明共用体 |
typedef | 给数据类型取别名 |
volatile | 说明变量在执行过程中可以隐含的改变 |
可以看到70%都是java中有的,学习起来并不是很难
2 数据类型
C中的数据类型可以分为以下几种
类型 | 描述 |
---|---|
基本数据类型 | 他们是算术类型,包含俩种类型,整数类型和浮点数类型 |
枚举类型 | 他们也是算术类型,被用来定义在程序中只能赋予其一定的离散整数值得变量 |
void | 标识没有可用的值 |
派生类型 | 他包括指针类型,数组类型,结构类型,共用体类型和函数类型 |
我们先介绍基本数据类型
整数类型
类型 | 存储大小 | 值范围 |
---|---|---|
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 |
各类型的储存大小,与系统位数有关,为了得到准确大小可以用sizeof
来计算
#include <stdio.h> #include <limits.h> int main(){ printf("int 存储大小 : %lu \n", sizeof(int)); return 0 ; } 复制代码
int 存储大小 : 4 复制代码
浮点类型
类型 | 存储大小 | 值范围 | 精度 |
---|---|---|---|
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位小数 |
void类型
类型 | 描述 |
---|---|
函数返回为空 | C 中有各种函数都不返回值,或者您可以说它们返回空。不返回值的函数的返回类型为空。例如 void exit (int status); |
函数参数为空 | C 中有各种函数不接受任何参数。不带参数的函数可以接受一个 void。例如 int rand(void); |
指针指向void | 类型为void*指针代表对象的地址,而不是类型,例如,内存分配函数 void *malloc( size_t size ); 返回指向 void 的指针,可以转换为任何数据类型。 |
3 变量
变量是程序可操作的储存区名称,C中每个变量都有特定的类型,类型决定了变量的大小和布局,该范围内的值都可以储存在内存中
C语言中有基本数据类型的变量,也可以有其他类型的变量,比如数组,指针,枚举,结构体,共用体等
变量的定义
变量的定义就是告诉储存器何处创建变量的储存,以及如何创建变量的储存,变量定义指定数据类型,并包含该类型的一个或多个变量列表
type variable_list; 复制代码
type必须是一个有效的C数据类型,variable_list为变量的名字,可以有多个
int i, j, k; char c, ch; float f, salary; double d; 复制代码
上方i,j,k等声明并定义了变量i,j,k
变量可以在声明的时候初始化
type variable_name = value; 例如 extern int d = 3, f = 5; // d 和 f 的声明与初始化 int d = 3, f = 5; // 定义并初始化 d 和 f byte z = 22; // 定义并初始化 z char x = 'x'; 复制代码
C中变量的声明
变量的声明,像编辑器保证变量以指定的类型和名称存在,这样编辑器就可以在不知道变量完整细节的情况下,也能进一步编译
变量声明有俩种情况
- 一种是需要建立储存空间的,例如:int a 在声明的时候就已经建立了存储空间。
- 另一种是不需要建立储存空间的,通过extern关键字声明变量名而不定义它,例如:extern int a 其中变量 a 可以在别的文件中定义的
- 除非有extern关键字,其他的都是变量的定义
extern int i; //声明,不是定义 int i; //声明,也是定义 复制代码
4 常量
常量是固定的值,在程序期间不会改变,这些固定的值又叫做字面量
常量可以是任何基本数据类型,常量在值定义之后不会修改
定义常量
在C中有俩种定义常量的方式
- 使用
#define
预处理器
#define identifier value #include <stdio.h> #include <limits.h> #define FFF 10 #define DDD 20 #define HHH '\n' int main(){ int a ; a =FFF*DDD; printf("值,%d",a); printf("\n字符串,%c",HHH); return 0 ; } 复制代码
值,200 字符串, 复制代码
- 使用
const
关键字
const type variable = value; int main() { const int FFF =10; const int DDD=20; const char HHH='\n'; int a; a = FFF * DDD; printf("值,%d", a); printf("\n字符串,%c", HHH); return 0; } 复制代码
值,200 字符串, 复制代码
5 储存类
储存类定义C中变量,函数的范围和生命周期,这些说明符放在他们所修饰的类型之前,下面列出可用的储存类
- auto
- register
- static
- extern
auto储存类
auto储存类是所有局部变量默认的储存类
{ int mount; auto int month; } 复制代码
上面的两种写法都是一样的,auto只能用在函数内,即只能修饰局部变量
register
用于定义储存在寄存器中而不是RAM的局部变量,这意味着变量的最大大小是寄存器的大小
{ register int miles; } 复制代码
寄存器只用于需要快速访问的变量,比如计数器
static
编译器在声明周期内保持保持局部变量的值,而不需要每次进入和离开作用域是创建和销毁,因此使用static修饰局部变量,可以函数调用间保持局部变量的值
static也可以用于全局变量,当static修饰全局变量时,变量的作用域会限制在他的本文件中,也就是只有本文件才可以访问(普通的全局变量,使用extern外部声明后任何文件都可以访问)
static函数:和上方的全局变量一样(非静态函数可以在另一个文件中直接引用,甚至不必使用extern声明)
extern
用于提供一个全局变量的引用,全局变量对所有的程序文件都可见
当在A文件定义一个全局变量a,B文件想要使用A文件的变量a,可以在B文件使用extern关键字拿到a的引用
第一个文件
#include <stdio.h> int count =5; extern void add(); int main() { add(); return 0; } 复制代码
第二个文件
#include <stdio.h> extern int count; void add(){ printf("count=%d\n",count); } 复制代码
运行后
bogon:untitled renxiaohui$ gcc text.c tex1.c bogon:untitled renxiaohui$ a.out count=5 复制代码
6 运算符
同java,不在记录
7 判断
同java
8 循环
同java
9 函数
定义函数
return_type function_name( parameter list ) { body of the function } 复制代码
一个函数的组成部分
- 返回值类型:一个函数可以返回一个值,
return_type
是一个函数返回值的数据类型,有些函数不返回数据,这种情况下return_type
的关键字是void
- 函数名称:函数的实际名称
function_name
- 函数参数:当调用函数是,需要向函数传递一个值,就是参数,参数可以有多个,也可以没有
- 函数主体:函数内实现逻辑的语句
int Max(int num1,int num2){ int result; if(num1>num2){ result=num1; }else{ result=num2; } return result; } 复制代码
这就是一个函数的简单实例
函数声明
函数的声明会告诉编译器,函数的名称和如何调用函数,函数的实际主体可以单独定义
函数声明包括以下几个部分
return_type function_name( parameter list ); 复制代码
针对上方的函数我们可以声明
int Max(int num1,int num2); 复制代码
在函数声明中,参数名称并不是很重要,可以省略掉
int Max(int ,int); 复制代码
当你在一个源文件定义一个函数,在另一个文件调用这个函数时,函数声明是必要的,这种情况下,你需要在调用函数文件的顶部声明函数
调用函数
#include <stdio.h> int Max(int,int); int main() { int num1 = 100; int num2 = 120; int rec; rec = Max(num1,num2); printf("比较结果为%d\n",rec); return 0; } int Max(int num1, int num2) { int result; if (num1 > num2) { result = num1; } else { result = num2; } return result; } 复制代码
结果为
比较结果为120 复制代码
C的作用域规则
局部变量
在某个函数块内声明的变量为局部变量,他只能被该函数或者该代码块内的函数语句使用,局部变量外部是不可知的
#include <stdio.h> int main () { /* 局部变量声明 */ int a, b; int c; /* 实际初始化 */ a = 10; b = 20; c = a + b; printf ("value of a = %d, b = %d and c = %d\n", a, b, c); return 0; } 复制代码
这里的a,b,c 都是局部变量
全局变量
全局变量定义在函数的外部,通常是程序的顶部,全局变量在整个程序的生命周期内都是有效的,在任意的函数内部可以访问全局变量。
全局变量可以被任意函数访问,也就是说全局变量在声明后在整个程序中都是可用的
局部变量和全局变量名称可以相同,但是在函数内,如果俩个名字相,会使用局部变量,不会使用全局变量
#include <stdio.h> //全局变量 int a = 10; int c = 40; int main(){ //局部变量 int a = 20; int b =30; printf("a=%d\n",a); printf("b=%d\n",b); printf("c=%d\n",c); return 0; } 复制代码
输出
a=20 b=30 c=40 复制代码
全局变量与局部变量的区别
- 全局变量保存在内存的全局储存区,占用静态的储存单元
- 局部变量保存在栈中,只有在函数被调用时,才动态的为变量分配储存单元
数组
声明数组
type arrayName [ arraySize ]; 复制代码
这是一个一维数组,arraySize必须是大于0的常量,type是任意有效的c数据类型,声明一个含有10个double数据的nums数组
double nums [10]; 复制代码
初始化数组
初始化固定数量的数组
int nums [3] = {10,2,3} 复制代码
初始化数量不固定的数组
int nums []={1,2,3,4,5} 复制代码
为某个数组赋值
nums[3] = 6; 复制代码
访问数组元素
int a = num[3]; 复制代码
实例
#include <stdio.h> int main(){ int n[10]; int i,j; for(i=0;i<10;i++){ n[i]=i*10; } for (int j = 0; j < 10; ++j) { printf("%d = %d\n",j,n[j]); } } 复制代码
结果
0 = 0 1 = 10 2 = 20 3 = 30 4 = 40 5 = 50 6 = 60 7 = 70 8 = 80 9 = 90 复制代码
枚举
枚举是C语言中的一种基本数据类型
enum 枚举名 {枚举元素1,枚举元素2,……}; 复制代码
假如我们定义一周七天,假如不用枚举,用常量
#define MON 1 #define TUE 2 #define WED 3 #define THU 4 #define FRI 5 #define SAT 6 #define SUN 7 复制代码
假如用枚举
enum Day{ MON=1, TUE, WED, THU, FRI, SAT, SUN } 复制代码
这样看起来会很简洁
注意:默认第一个成员变量为0,后面的顺序加1,如果第一个是1,后面是2,以此类推
枚举变量的定义
1 先定义枚举类型在定义变量
enum DAY{ MON=1, TUE, WED, THU, FRI, SAT, SUN }; enum DAY day; 复制代码
2 定义枚举类型的同时定义枚举变量
enum DAY { MON=1, TUE, WED, THU, FRI, SAT, SUN } day; 复制代码
3 省去枚举名称直接定义枚举变量
enum { MON=1, TUE, WED, THU, FRI, SAT, SUN } day; 复制代码
实例
#include <stdio.h> enum DAY { MON = 1, TUE, WED, THU, FRI, SAT, SUN }; int main() { enum DAY day; day=THU; printf("enun= %d\n",day); return 0; } 复制代码
结果
enun= 4 复制代码
在C语言中,枚举是被当做int
或者 unsigned int
来处理的,所以按照C语言规范是没有办法遍历枚举的
枚举在switch中使用
#include <stdio.h> #include <stdlib.h> enum Color { red=1, green, blue }; int main() { enum Color enumColor; printf("请选择你喜欢的颜色(1 红色 2 绿色 3 蓝色)"); scanf("%d",&enumColor); switch (enumColor){ case red: printf("你喜欢红色\n"); break; case green: printf("你喜欢绿色\n"); break; case blue: printf("你喜欢蓝色\n"); break; } return 0; } 复制代码
结果
请选择你喜欢的颜色(1 红色 2 绿色 3 蓝色)1 你喜欢红色 复制代码
指针
每一个变量都有一个内存位置,每一个内存位置都定义了可使用&符号
访问地址,他表示在内存中的一个地址
#include <stdio.h> int main(){ int a ; int b[10]; printf("a 的内存地址=%p\n",&a); printf("b 的内存地址=%p\n",&b); return 0; } 复制代码
结果
a 的内存地址=0x7ffee5e086c8 b 的内存地址=0x7ffee5e086d0 复制代码
什么是指针
指针是一个变量,其值为另一个变量的内存地址,使用指针之前需要对其进行声明
type *var-name; 复制代码
type
是指针的类型,他必须是一个有效的C数据类型,var-name
是指针的名称,*
用来表示这个变量是指针。
int *aa;//一个整形指针 double *bb;//一个double类型的指针 复制代码
如何使用指针
使用指针会频繁的进行如下几个操作,定义一个指针变量,把变量地址赋值给指针,访问指针变量中的可用地址
#include <stdio.h> int main(){ int a =20;//定义int值 int *b;//定义指针 b=&a;//为指针赋值 printf("变量的地址值=%p\n",&a); printf("指针的值=%p\n",b); printf("指针的地址值=%d\n",*b); return 0; } 复制代码
结果
变量的地址值=0x7ffeef4356f8 指针的值=0x7ffeef4356f8 指针的地址值=20 复制代码
C中的NULL指针
变量声明的时候,如果没有明确的地址可以赋值,为指针赋值一个NULL是一个良好的习惯,赋值为NULL被称为空指针
int main(){ int *var = NULL; printf("var的地址为=%p\n",var); return 0; } 复制代码
var的地址为=0x0 复制代码
指针指向指针
#include <stdio.h> int main(){ int a; int *b; int **c; a=40; b=&a; c=&b; printf("a的值为=%d\n",a); printf("b的值为=%d\n",*b); printf("c的值为=%d\n",**c); return 0; } 复制代码
a的值为=40 b的值为=40 c的值为=40 复制代码
传递数组给函数,函数返回数组
#include <stdio.h> int sum(int *arr,int size); int* getarr(); int main() { int a[4] = {1, 2, 3, 4}; int v = sum(a,4); printf("sum=%d\n",v); int *p = getarr(); for (int i = 0; i < 4; ++i) { printf("数组=%d\n",*(p+i)); printf("数组=%d\n",p[i]); } return 0; } int sum(int *arr, int size) { int sum = 0; for (int i = 0; i < size; ++i) { printf("i=%d\n", arr[i]); printf("i=%d\n", *(arr+i)); sum+=arr[i]; } return sum; } int * getarr(){ static int arr[4]={2,4,5,7}; return arr; } 复制代码
i=1 i=1 i=2 i=2 i=3 i=3 i=4 i=4 sum=10 数组=2 数组=2 数组=4 数组=4 数组=5 数组=5 数组=7 数组=7 复制代码
指针运算
C指针用数字表示地址,因此可以进行算术运算,++,--,+,-等,假如prt是一个int类型的地址1000,那么执行prt++,prt将指向1004,即当前位置移动4个字节,假如prt是一个char类型的地址1000,那么执行prt++,prt将指向1001,这个跟类型也是相关的
- 指针的每一次递增,他实际上会指向下一个元素的储存单元
- 指针的每一次递减,他实际上指向上一个元素的储存单元
- 指针在递增和递减跳跃的字节数,取决于指针所指向变量的数据类型的长度,比如int就是4个字节
递增一个指针
#include <stdio.h> const int Max = 3; int main() { int arr[3] = {1, 2, 3}; int i ,*p; //给p指针赋值数组中第一个元素的地址 p = arr; for (int i = 0; i < Max; ++i) { printf("元素的地址=%p\n", p); printf("元素的地址=%d\n", *p); //移动到下个位置 p++; } return 0; } 复制代码
元素的地址=0x7ffee165b6ac 元素的地址=1 元素的地址=0x7ffee165b6b0 元素的地址=2 元素的地址=0x7ffee165b6b4 元素的地址=3 复制代码
指针数组
有一种情况,我们数组内可以存储内存地址值
#include <stdio.h> const int Max= 3; int main(){ int arr[3]={1,2,3}; int *p[Max]; for (int i = 0; i <Max ; ++i) { p[i]=&arr[i]; } for (int j = 0; j < Max; ++j) { printf("指针数组数据=%p\n",p[j]); printf("指针数组数据值=%d\n",*p[j]); } return 0; } 复制代码
指针数组数据=0x7ffee7cda6ac 指针数组数据值=1 指针数组数据=0x7ffee7cda6b0 指针数组数据值=2 指针数组数据=0x7ffee7cda6b4 指针数组数据值=3 复制代码
指向指针的指针
指针也可以指向指针
#include <stdio.h> int main(){ int a = 10; int *b; int **c; b=&a; c=&b; printf("a的值为=%d\n",a); printf("b的值为=%d\n",*b); printf("c的值为=%d\n",**c); } 复制代码
a的值为=10 b的值为=10 c的值为=10 复制代码
传递指针给函数
#include <stdio.h> #include <time.h> void getlong(unsigned long *a); int main() { unsigned long b; getlong(&b); printf("b的值为=%ld\n",b); } void getlong(unsigned long *a) { *a = time(NULL); return; } 复制代码
b的值为=1585048748 复制代码
函数指针和回调函数
函数指针是指向函数的指针变量,函数指针可以向普通函数一样,传递参数,调用函数
函数返回值类型 (* 指针变量名) (函数参数列表); 复制代码
#include <stdio.h> int max(int, int); int main() { //p是函数指针 int (*p)(int, int) = &max;//&可以省略 int a, b, c, d; printf("请输入三个数字:"); scanf("%d,%d,%d", &a, &b, &c); d = p(p(a, b), c);//相当于调用max(max(a,b),c); printf("d的值为=%d\n", d); return 0; } int max(int a, int b) { return a > b ? a : b; } 复制代码
输出
请输入三个数字:1,2,3 d的值为=3 复制代码
将指针函数当做参数传递给函数
#include <stdio.h> #include <stdlib.h> void setvalue(int *arr, int b, int(*p)(void)) { for (int i = 0; i < b; ++i) { arr[i] = p(); } } int stett(int(*p)(void)){ return p(); } int getvalue(void) { return rand(); } int main() { int arr[10]; setvalue(arr, 10, getvalue); for (int i = 0; i < 10; ++i) { printf("i=%d\n", arr[i]); } int b; b= stett(getvalue); printf("b=%d\n", b); return 0; } 复制代码
结果
i=16807 i=282475249 i=1622650073 i=984943658 i=1144108930 i=470211272 i=101027544 i=1457850878 i=1458777923 i=2007237709 b=823564440 复制代码
字符串
C语言定义字符串Hello,两种形式,字符串和字符数组的区别:最后一位是否是空字符
#include <stdio.h> int main(){ char hello[6]= {'H','e','l','l','o','\0'}; char hello1[]= "hello"; char *hello2="hello"; printf("测试=%s\n",hello); printf("测试=%s\n",hello1); printf("测试=%s\n",hello2); return 0; } 复制代码
测试=Hello 测试=hello 测试=hello 复制代码
结构体
结构体可以存储不同类型的数据项
定义一个结构体
struct tag { member-list member-list member-list ... } variable-list ; 复制代码
- tag:结构体标签
- member-list:结构体数据项
- variable-list:结构体变量
struct Book { int book_id ; char title[50]; char author[50]; char subject[50]; } book; 复制代码
在一般情况下,tag、member-list、variable-list 这 3 部分至少要出现 2 个。以下为实例:
//此声明声明了拥有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 SIMPLE t1, t2[20], *t3; //也可以用typedef创建新类型 typedef struct { int a; char b; double c; } Simple2; //现在可以用Simple2作为类型声明新的结构体变量 Simple2 u1, u2[20], *u3; 复制代码
结构体中可以含有其他结构体和指针
//结构体包含其他结构体 struct AA{ int a; struct Book b; }; //结构体包含自己的指针 struct BB{ int b; struct BB *nextbb }; 复制代码
结构体的初始化
struct Book { int book_id ; char title[50]; char author[50]; char subject[50]; } book= {1,"c语言","小明","bb"}; 复制代码
title : c语言 author: 小明 subject: bb book_id: 1 复制代码
访问结构体的成员
访问结构体的成员可以用.
符号,比如上方的book.title;
int main(){ struct Book book1; strcpy(book1.title,"学习书"); strcpy(book1.author,"小红"); strcpy(book1.subject,"111"); book1.book_id=222; printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book1.title, book1.author, book1.subject, book1.book_id); return 0; } 复制代码
title : 学习书 author: 小红 subject: 111 book_id: 222 复制代码
结构体作为函数的参数
void printstuct(struct Book book); int main(){ struct Book book1; strcpy(book1.title,"学习书"); strcpy(book1.author,"小红"); strcpy(book1.subject,"111"); book1.book_id=222; printf("title : %s\nauthor: %s\nsubject: %s\nbook_id: %d\n", book1.title, book1.author, book1.subject, book1.book_id); printstuct(book1); return 0; } void printstuct(struct Book 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); } 复制代码
title : 学习书 author: 小红 subject: 111 book_id: 222 Book title : 学习书 Book author : 小红 Book subject : 111 Book book_id : 222 复制代码
指向结构体的指针
定义,赋值,调用
struct Books *struct_pointer; struct_pointer = &Book1; struct_pointer->title; 复制代码
int main() { struct Book book1; strcpy(book1.title, "学习书"); strcpy(book1.author, "小红"); strcpy(book1.subject, "111"); book1.book_id = 222; printstuct1(&book1); return 0; } void printstuct1(struct Book *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 : 学习书 Book author : 小红 Book subject : 111 Book book_id : 222 复制代码
共用体
共用体是一个特殊的数据类型,允许在相同的储存位置,储存不同的数据类型,可以定义一个带有多个成员的共用体,但是任何时候只能一个成员带值
定义共用体
用union
定义共用体,
union [union tag] { member definition; member definition; ... member definition; } [one or more union variables]; 复制代码
union tag
是可选的,每个member definition;
都是标准的变量定义,如int i char b等,在分号之前可以定义一个或多个共用体变量是可选的
定义一个成员有int,float,char[]的共用体
#include <stdio.h> #include <string.h> union Data { int a; char b[100]; float c; }; int main() { union Data data; printf("数据长度=%lu\n", sizeof(data)); data.a = 1; data.c = 10.00; strcpy(data.b, "测试数据"); printf("数据%d\n",data.a); printf("数据%f\n",data.c); printf("数据%s\n",data.b); return 0; } 复制代码
数据长度=100 数据-393497114 数据-5278115000342806695772160.000000 数据测试数据 复制代码
我们看到数据a,c成员的值有损坏,是因为最后赋值的变量占用了的内存位置,这也是b变量可以正确输出的原因
我们同一时间只能使用一个变量
#include <stdio.h> #include <string.h> union Data { int a; char b[100]; float c; }; int main() { union Data data; printf("数据长度=%lu\n", sizeof(data)); data.a = 1; printf("数据%d\n",data.a); data.c = 10.00; printf("数据%f\n",data.c); strcpy(data.b, "测试数据"); printf("数据%s\n",data.b); return 0; } 复制代码
数据长度=100 数据1 数据10.000000 数据测试数据 复制代码
在这里所有的成员都可以正确输出,是因为同一时间只用到了一个变量
typedef
C语言提供typedef
关键字,可以使用它为类型起一个新的名字
#include <stdio.h> typedef unsigned int TEXT; int main(){ TEXT a = 11; printf("参数为=%d\n",a); return 0; } 复制代码
参数为=11 复制代码
也可以为用户自定义的数据类型去一个新的名字,比如结构体
#include <stdio.h> #include <string.h> typedef unsigned int TEXT; typedef struct BOOKS { char a[50]; char b[50]; } Book; int main() { TEXT a = 11; printf("参数为=%d\n", a); Book book; strcpy(book.a, "测试1"); strcpy(book.b, "测试2"); printf("a=%s\n", book.a); printf("b=%s\n", book.b); return 0; } 复制代码
参数为=11 a=测试1 b=测试2 复制代码
typedef和#define 的区别
#define
是一个C指令,用于为各种数据类型定义别名,与typedef
类似,但是他有以下几种不同
typede
f仅限于为类型定义符号名称,#define
不仅为类型定义别名,也可以为数值定义别名typedef
为编译器解释执行,#define
为预编译器进行处理
#define TRUE 0 #define FALSE 1 int main() { printf("数值为=%d\n", TRUE); printf("数值为=%d\n", FALSE); return 0; } 复制代码
数值为=0 数值为=1 复制代码
输入和输出
getchar() & putchar() 函数
int getchar(void)
函数从屏幕读取下一个可用的字符,并把它返回为一个整数。这个函数在同一个时间内只会读取一个单一的字符。您可以在循环内使用这个方法,以便从屏幕上读取多个字符。
int putchar(int c)
函数把字符输出到屏幕上,并返回相同的字符。这个函数在同一个时间内只会输出一个单一的字符。您可以在循环内使用这个方法,以便在屏幕上输出多个字符。
#include <stdio.h> int main(){ printf("请输入一个字符\n"); int a = getchar(); printf("你的输入为\n"); putchar(a); printf("\n"); } 复制代码
请输入一个字符 text 你的输入为 t 复制代码
gets() & puts() 函数
char *gets(char *s)
函数从stdin读取一行到s指向的缓存区,直到一个终止符或一个EOF
int puts(const char *s)
函数吧一个字符串s和一个尾随的换行符写入stdout
#include <stdio.h> int main(){ char a[100]; printf("输入你的字符\n"); gets(a); printf("你输入的字符为\n"); puts(a); return 0; } 复制代码
输入你的字符 text 你输入的字符为 text 复制代码
scanf() 和 printf() 函数
int scanf(const char *format, ...)
函数从标准输入流 stdin 读取输入,并根据提供的 format 来浏览输入。
int printf(const char *format, ...)
函数把输出写入到标准输出流 stdout ,并根据提供的格式产生输出。
format 是一个简单的常量字符串,但是你可以定义%s,%d,%c,%f来读取字符串,数字,字符,浮点数
#include <stdio.h> int main() { char a[100]; int b; printf("输入文字\n"); scanf("%s%d", a, &b); printf("你输入的文字=%s,%d\n",a,b); return 0; } 复制代码
输入文字 text 123 你输入的文字=text,123 复制代码
文件读写
#include <stdio.h> int main() { FILE *file; //打开文件允许读写,如果不存在则创建该文件 file = fopen("/Users/renxiaohui/Desktop/test.txt", "w+"); //写入一行 fprintf(file, "this is a text\n"); //再写入一行 fputs("this is a text aaaa\n", file); fclose(file); } 复制代码
会在创建文件test.txt,并写入俩行文字
#include <stdio.h> int main() { FILE *file1; char a[255]; //打开一个文件,只读 file1 = fopen("/Users/renxiaohui/Desktop/test.txt", "r"); //读取文件,当遇到第一个空格或者换行符会停止读取 fscanf(file1, "%s", a); printf("1= %s\n", a); //读取字符串到缓冲区,遇到换行符或文件的末尾 EOF返回 fgets(a, 255, file1); printf("2= %s\n", a); fgets(a, 255, file1); printf("3= %s\n", a); //关闭文件 fclose(file1); } 复制代码
读取刚才创建的文件
1= this 2= is a text 3= this is a text aaaa 复制代码
预处理器
C预处理器不是编译器的组成部分,他是编译过程中的一个单独步骤,他们会在编译器实际编译之前完成所需的预处理
所有的预处理器都是以(#)开头的,他必须是第一个非空字符,为了增强可读性,预处理器应该从第一列开始,下面列出比较重要的预处理器
指令 | 描述 |
---|---|
#define | 定义宏 |
#undef | 取消定义的宏 |
#include | 包含一个源文件代码 |
#ifdef | 如果宏已经定义则返回真 |
#ifndef | 如果宏没有定义则返回真 |
#if | 如果给定条件为真则编译一下代码 |
#else | #if的替代方案 |
#elseif | 如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码 |
#endif | 结束一个#if。。#else 语句 |
#error | 遇到标准错误是,输出错误消息 |
#pragma | 使用标准化方法,向编译器发布特殊指令到编译器中去 |
预编译器实例
#define MAX 20 复制代码
这个指令表示,把所有的MAX定义为20,使用#define定义常量可以增加可读性
#include <stdio.h> #include "myheader.h" 复制代码
第一个表示从系统库获取stdio.h
库,并添加到源文件中,一个是从本地目录获取myheader.h
,并添加到源文件中
#undef FILE #define FILE 99 复制代码
取消已经定义的FILE,并把它重新定义为99
#ifdef MESSAGE #define MESSAGE "you wish" #endif 复制代码
这个表示只有MESSAGE未定义时,才定义MESSAGE为you wish
预定义宏
ANSI C中预定义宏,我们可以在编程中使用这些宏,但不可以改变他的值
宏 | 描述 |
---|---|
1__DATE__ | 当前日期,一个以 "MMM DD YYYY" 格式表示的字符常量。 |
1__TIME__ | 当前时间,一个以 "HH:MM:SS" 格式表示的字符常量。 |
1__FILE__ | 这会包含当前文件名,一个字符串常量。 |
1__LINE__ | 这会包含当前行号,一个十进制常量。 |
1__STDC__ | 当编译器以 ANSI 标准编译时,则定义为 1。 |
看下使用
#include <stdio.h> int main() { printf("data=%s\n",__DATE__); printf("file=%s\n",__FILE__); printf("time=%s\n",__TIME__); printf("line=%d\n",__LINE__); printf("stdic=%d\n",__STDC__); return 0; } 复制代码
data=Mar 31 2020 file=text27.c time=15:31:49 line=19 stdic=1 复制代码
参数化宏
参数化宏可以模拟函数,例如下面是一个计算数字平方的方法
int square(int x) { return x * x; } 复制代码
可以用参数化宏来表示
#define square(x) ((x)*(x)) 复制代码
在使用参数化宏之前必须使用#define来定义,参数列表必须包含在圆括号内的,并且必须紧跟在宏名称之后,不允许有空格
#define square(x) ((x)*(x)) int main() { printf("平方为=%d\n",square(5)); return 0; } 复制代码
平方为=25 复制代码
预处理运算符
宏延续运算符(\)
一个宏通常写在一个单行上,但是如果宏太长,一个单行容不下,则使用宏延续运算符(\)例如
#define message_for(a,b) \ printf(#a"and "#b"we are friend\n") 复制代码
字符串常量化运算符(#)
在宏定义中,需要把一个宏的参数转化为字符串常量时,则使用字符串常量化运算符(#)例如:
#define message_for(a,b) \ printf(#a"and "#b"we are friend\n") int main() { message_for("xiaoming","xiaohong"); return 0; } 复制代码
"xiaoming"and "xiaohong"we are friend 复制代码
** 标记粘贴运算符(##)**
直接看代码比较
#define add(n) printf("token" #n " = %d\n",token##n) int main() { int token34 =40; add(34); return 0; } 复制代码
token34 = 40 复制代码
这个是怎么发生的,因为这个实际会从编译器中产出以下实际输出
printf ("token34 = %d", token34); 复制代码
defined()运算符
预处理器defined运算符是在常量表达式中的,用来确定一个标识符是否已经#define定义过,如果定义过为真,如果没有定义过值为假
#if !defined(TEXT) #define TEXT "text\n" #endif int main() { printf(TEXT); return 0; } 复制代码
text 复制代码
头文件
这篇关于Android 音视频入门:C语言基础的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-01-18android.permission.read_media_video
- 2024-01-18android_getaddrinfo failed eai_nodata
- 2024-01-18androidmo
- 2024-01-15Android下三种离屏渲染技术
- 2024-01-09Android 蓝牙使用
- 2024-01-06Android对接华为AI - 文本识别
- 2023-11-15代码安全之代码混淆及加固(Android)
- 2023-11-10简述Android语音播报TTS
- 2023-11-06Android WiFi工具类
- 2023-07-22Android开发未来的出路