Android 音视频入门:C语言基础

2020/3/31 23:02:23

本文主要是介绍Android 音视频入门:C语言基础,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

概述

C语言对于Android开发来说还是非常必要的,不管你是要阅读源码,还是想要学习NDK,音视频,性能优化等,都不可避免需要接触到C,而且C语言属于系统级的语言,操作系统内核都有C的身影,所以我今天学习一下C语言,本篇博客作为笔记,以防以后忘记

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&emsp;枚举名&emsp;{枚举元素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类似,但是他有以下几种不同

  • typedef仅限于为类型定义符号名称,#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语言基础的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!


扫一扫关注最新编程教程