Error[8]: Undefined offset: 3028, File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 121
File: /www/wwwroot/outofmemory.cn/tmp/plugin_ss_superseo_model_superseo.php, Line: 473, decode(

菜鸟教程之C语言教程
Android JNI和NDK学习(基础篇):C语言基础

前言

C语言对于Android开发来说还是非常必要的,不管你是要阅读源码,还是想要学习NDK,音视频,性能优化等,都不可避免需要接触到C;
而且C语言属于系统级的语言, *** 作系统内核都有C的身影,作为一名科班出身的程序员,在毕业7年以后决定重新捡起这门语言。

一、C语言简介 1.1 C语言特点

C语言最初适用于系统开发工作的,特别是组成 *** 作系统的程序,由于C语言产生的代码运行速度与汇编编写的代码运行速度几乎相同,所以采用C语言作为系统开发语言
下面列举几个使用C的实例

1.2 C语言开发环境

市面上能够进行C语言开发的编译器非常多,最经典的当然还是VC6.0,还有VS系列,以及MAC上的XCode,这里我使用的是CodeBlock;
关于编译器的下载和安装,请自行解决

1.3 Hello World
#include 
#include 

int main()
{
    /*
    我是一行注释
    */
    printf("Hello world!\n");
    return 0;
}

一个C程序一般包括以下几个部分:

二、C语言入门 2.1 标识符

标记符是用来标记变量,函数或者任何用户自定的变量名称,一个标识符以字母A—Z,a-z,或者_下划线开始,后面跟0个或多个字母,数字,下划线

标识符中不允许出现标点字符,比如@%,标识符是区分大小写的

2.2 关键字

这些关键字不能作为常量名或者变量名,其他标识符的名称

关键字说明
continue结束当前循环,开始下一轮循环
switch用于开关语句
case开关语句分支
default开关语句中的其他分支
break跳出当前循环
do循环语句的循环体
while循环语句的循环条件
if条件语句
else条件语句否定分支与if一起使用
for一种循环语句
goto无条件跳转语句
return子程序返回语句,可带参数可不带参数
char声明字符变量或者返回值类型
double声明双精度浮点类型对象或函数返回值类型
float声明浮点型变量或返回值类型
short声明短整形变量或者返回值类型
int声明整形变量或者返回值类型
long声明长整形变量或返回值类型
unsigned声明无符号类型变量或返回值类型
void声明函数无返回值或无参数,声明无类型指针
enum声明枚举类型
static声明静态变量
void声明函数无返回值或无参数,声明无类型指针
auto自动声明变量
const定义常量,如果一个变量被const修饰,则它的值不能被改变
extern声明变量或函数在其他文件或本文件的其他位置定义
register声明寄存器变量
signed声明有符号类型的变量
sizeof计算数据类型或变量的长度
struct声明结构体类型
union声明共用体
typedef给数据类型取别名
volatile说明变量在执行过程中可以隐含的改变

可以看到70%都是java中有的,学习起来并不是很难

2.3 数据类型

C中的数据类型可以分为以下几种

2.3.1 整数类型
类型存储大小值范围
char1字节-128-127或0-255
unsigned char1字节0-255
signed char1字节-128-127
int2或4字节-32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647
unsigned int2或4字节0 到 65,535 或 0 到 4,294,967,295
short2字节-32,768 到 32,767
unsigned short2字节0 到 65,535
long4字节-2,147,483,648 到 2,147,483,647
unsigned long4字节0 到 4,294,967,295

各类型的储存大小,与系统位数有关,为了得到准确大小可以用sizeof来计算

#include 
#include 
int main(){
    printf("int 存储大小 : %lu \n", sizeof(int));
    return 0 ;
}

int 存储大小 : 4

2.3.2 浮点类型
类型存储大小值范围精度
float4字节1.2E-38 到 3.4E+386 位小数
double8字节2.3E-308 到 1.7E+30815位小数
long double16字节3.4E-4932 到 1.1E+493219位小数
2.3.3 void类型
类型描述
函数返回为空C 中有各种函数都不返回值,或者您可以说它们返回空。不返回值的函数的返回类型为空。例如 void exit (int status);
函数参数为空C 中有各种函数不接受任何参数。不带参数的函数可以接受一个 void。例如 int rand(void);
指针指向void类型为void*指针代表对象的地址,而不是类型,例如,内存分配函数 void *malloc( size_t size ); 返回指向 void 的指针,可以转换为任何数据类型。
2.4 变量

变量是程序可 *** 作的储存区名称,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 常量

常量是固定的值,在程序期间不会改变,这些固定的值又叫做字面量

常量可以是任何基本数据类型,常量在值定义之后不会修改

4.1 定义常量

在C中有俩种定义常量的方式

#define identifier value

#include 
#include 

#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
字符串,
4.2 使用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;
}
5 储存类

储存类定义C中变量,函数的范围和生命周期,这些说明符放在他们所修饰的类型之前,下面列出可用的储存类

5.1 auto储存类

auto储存类是所有局部变量默认的储存类

{
   int mount;
   auto int month;
}

上面的两种写法都是一样的,auto只能用在函数内,即只能修饰局部变量

5.2 register

用于定义储存在寄存器中而不是RAM的局部变量,这意味着变量的最大大小是寄存器的大小

{
   register int  miles;
}

寄存器只用于需要快速访问的变量,比如计数器

5.3 static

编译器在声明周期内保持保持局部变量的值,而不需要每次进入和离开作用域是创建和销毁,因此使用static修饰局部变量,可以函数调用间保持局部变量的值

static也可以用于全局变量,当static修饰全局变量时,变量的作用域会限制在他的本文件中,也就是只有本文件才可以访问(普通的全局变量,使用extern外部声明后任何文件都可以访问)

static函数:和上方的全局变量一样(非静态函数可以在另一个文件中直接引用,甚至不必使用extern声明)

5.4 extern

用于提供一个全局变量的引用,全局变量对所有的程序文件都可见

当在A文件定义一个全局变量a,B文件想要使用A文件的变量a,可以在B文件使用extern关键字拿到a的引用

第一个文件

#include 

int count =5;

extern void add();

int main() {
    add();
    return 0;
}

第二个文件

#include 

extern int count;

void add(){
    printf("count=%d\n",count);
}

count=5
6 运算符

同java,不再记录

7 判断

同java

8 循环

同java

9 函数 9.1 定义函数
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;
}
9.2 函数声明

函数的声明会告诉编译器,函数的名称和如何调用函数,函数的实际主体可以单独定义

函数声明包括以下几个部分

return_type function_name( parameter list );

针对上方的函数我们可以声明

int Max(int num1,int num2);

在函数声明中,参数名称并不是很重要,可以省略掉

int Max(int ,int);

当你在一个源文件定义一个函数,在另一个文件调用这个函数时,函数声明是必要的,这种情况下,你需要在调用函数文件的顶部声明函数

9.3 函数声明调用函数
#include 

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
10 C的作用域规则 10.1 局部变量

在某个函数块内声明的变量为局部变量,他只能被该函数或者该代码块内的函数语句使用,局部变量外部是不可知的

#include 
 
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 都是局部变量

10.2 全局变量

全局变量定义在函数的外部,通常是程序的顶部,全局变量在整个程序的生命周期内都是有效的,在任意的函数内部可以访问全局变量。
全局变量可以被任意函数访问,也就是说全局变量在声明后在整个程序中都是可用的
局部变量和全局变量名称可以相同,但是在函数内,如果俩个名字相,会使用局部变量,不会使用全局变量

#include 

//全局变量
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
10.3 全局变量与局部变量的区别 11 数组 11.1 声明数组
type arrayName [ arraySize ];

这是一个一维数组,arraySize必须是大于0的常量,type是任意有效的c数据类型,声明一个含有10个double数据的nums数组

double nums [10];
11.2 初始化数组

初始化固定数量的数组

int nums [3] = {10,2,3}

初始化数量不固定的数组

int nums []={1,2,3,4,5}

为某个数组赋值

nums[3] = 6;

访问数组元素

int a = num[3];

实例

#include 


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
12 枚举

枚举是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,以此类推

12.1 枚举变量的定义

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 
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语言规范是没有办法遍历枚举的

12.2 枚举在switch中使用
#include 
#include 

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
你喜欢红色
13 指针

每一个变量都有一个内存位置,每一个内存位置都定义了可使用&符号访问地址,他表示在内存中的一个地址

#include 

int main(){

    int a ;

    int b[10];

    printf("a 的内存地址=%p\n",&a);
    printf("b 的内存地址=%p\n",&b);

    return 0;
}
a 的内存地址=0x7ffee5e086c8
b 的内存地址=0x7ffee5e086d0
13.1 什么是指针

指针是一个变量,其值为另一个变量的内存地址,使用指针之前需要对其进行声明

type *var-name;

type是指针的类型,他必须是一个有效的C数据类型,var-name是指针的名称,*用来表示这个变量是指针。

int *aa;//一个整形指针
double  *bb;//一个double类型的指针
13.2 如何使用指针

使用指针会频繁的进行如下几个 *** 作,定义一个指针变量,把变量地址赋值给指针,访问指针变量中的可用地址

#include 

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
13.3 C中的NULL指针

变量声明的时候,如果没有明确的地址可以赋值,为指针赋值一个NULL是一个良好的习惯,赋值为NULL被称为空指针

int main(){
    int *var = NULL;

    printf("var的地址为=%p\n",var);

    return 0;
}
13.4 指针型指针
#include 

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
13.5 传递数组给函数,函数返回数组
#include 

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
13.6 指针运算

C指针用数字表示地址,因此可以进行算术运算,++,–,+,-等,假如prt是一个int类型的地址1000,那么执行prt++,prt将指向1004,即当前位置移动4个字节,假如prt是一个char类型的地址1000,那么执行prt++,prt将指向1001,这个跟类型也是相关的

递增一个指针

#include 

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
13.7 指针数组

有一种情况,我们数组内可以存储内存地址值

#include 

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
13.7 传递指针给函数
#include 
#include 

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
14 函数指针 14.1 指向函数的指针

函数指针是指向函数的指针变量,函数指针可以向普通函数一样,传递参数,调用函数

函数返回值类型 (* 指针变量名) (函数参数列表);

#include 

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;
}
14.2 将指针函数当做参数传递给函数
#include 
#include 

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
15 字符串

C语言定义字符串Hello,两种形式,字符串和字符数组的区别:最后一位是否是空字符

#include 

int main(){
    char hello[6]= {'H','e','l','l','o','}';char
    [ hello1]="hello" ;char
    * =hello2"hello";printf
    ("测试=%s\n",)hello;printf
    ("测试=%s\n",)hello1;printf
    ("测试=%s\n",)hello2;return
    0 ;}
测试=Hello
测试=hello
测试=hello

struct
16 结构体 16.1 结构体的定义

定义一个结构体

tag - { 
    member-list
    member-list 
    member.list  
    ..}
- variable;list 
  • tag:结构体标签
  • Book int {
        ; book_id char
        [ title50];char
        [ author50];char
        [ subject50];}
    ; book//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c
    

    在一般情况下,tag、member-list、variable-list 这 3 部分至少要出现 2 个。以下为实例:

    //同时又声明了结构体变量s1
    //这个结构体并没有标明其标签
    struct
    int 
    {
        ; achar
        ; bdouble
        ; c}
    ; s1//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c
     
    //结构体的标签被命名为SIMPLE,没有声明变量
    struct
    SIMPLE int
    {
        ; achar
        ; bdouble
        ; c}
    ;//用SIMPLE标签的结构体,另外声明了变量t1、t2、t3
    struct
    SIMPLE , t1[ t220],* ;t3//也可以用typedef创建新类型
     
    typedef
    struct int
    {
        ; achar
        ; bdouble
        ; c} 
    ; Simple2//现在可以用Simple2作为类型声明新的结构体变量
    ,
    Simple2 u1[ u220],* ;u3//结构体包含其他结构体
    
    16.2 结构体中可以含有其他结构体和指针
    struct
    AA int{
        ; astruct
        Book ; b}
    ;//结构体包含自己的指针
    struct
    BB int{
        ; bstruct
        BB * }nextbb
    ;struct
    
    16.3 结构体的初始化
    Book int {
        ; book_id char
        [ title50];char
        [ author50];char
        [ subject50];}
    = book1 {,"c语言","小明","bb"};title : c语言
    author: 小明
    subject: bb
    book_id: 1
    
    
    int
    16.4 访问结构体的成员

    访问结构体的成员可以用.符号,比如上方的book.title;

    main ()struct{
        Book ; book1strcpy
        (.book1,title"学习书");strcpy
        (.book1,author"小红");strcpy
        (.book1,subject"111");.
        book1=book_id222;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
    16.5 结构体作为函数的参数
    
    printstuct (structBook ) book;int
    
    main ()struct{
    
        Book ; book1strcpy
    
        (.book1,title"学习书");strcpy
        (.book1,author"小红");strcpy
        (.book1,subject"111");.
    
        book1=book_id222;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 (structBook ) bookprintf{
        ("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: 小红
    subject111 :
    book_id222 :
    Book title : 学习书
    Book author : 小红
    Book subject 111 :
    Book book_id 222 struct
    
    16.6 指向结构体的指针

    定义,赋值,调用

    Books * ;struct_pointer=
    
    struct_pointer & ;Book1;
    
    struct_pointer->titleint
    
    main ()struct {
    
        Book ; book1strcpy
    
        (.book1,title"学习书" );strcpy
        (.book1,author"小红" );strcpy
        (.book1,subject"111" );.
    
        book1=book_id 222 ;printstuct1
        (&)book1;return
        0 ;}
    void
    
    printstuct1 (structBook * )bookprintf {
        ("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
    
    17 共用体

    共用体是一个特殊的数据类型,允许在相同的储存位置,储存不同的数据类型,可以定义一个带有多个成员的共用体,但是任何时候只能一个成员带值

    17.1 定义共用体

    用union定义共用体

    [ union] tag;
    {
       member definition;
       member definition.
       ..;
       member definition}
    [ unionone or more ] variables;#
    

    union tag是可选的,每个member definition,都是标准的变量定义,如int i ; char b等,在分号之前可以定义一个或多个共用体变量是可选的

    定义一个成员有int,float,char[]的共用体

    
    include# 
    includeunion 
    
    int Data {
        ; achar
        [ b100];float
        ; c}
    ;int
    
    main ()union {
    
        ; Data dataprintf
        ("数据长度=%lu\n",sizeof ()data);.
    
        data=a 1 ;.
        data=c 10.00 ;strcpy
        (.data,b"测试数据" );printf
        
        ("数据 a = %d\n",.data)a;printf
        ("数据 c = %f\n",.data)c;printf
        ("数据 b = %s\n",.data)b;return
        0 ;}
    数据长度=100
    数据 a = -393497114
    数据 c = -5278115000342806695772160.000000
    数据 b = 测试数据
    
    
    
    #

    可以看到数据a,c成员的值有损坏,是因为最后赋值的变量占用了的内存位置,这也是b变量可以正确输出的原因。

    我们同一时间只能使用一个变量

    
    include# 
    includeunion 
    
    int Data {
        ; achar
        [ b100];float
        ; c}
    ;int
    
    main ()union {
    
        ; Data dataprintf
        ("数据长度=%lu\n",sizeof ()data);.
    
        data=a 1 ;printf
        ("数据 a = %d\n",.data)a;.
    
        data=c 10.00 ;printf
        ("数据 c = %f\n",.data)c;strcpy
    
        (.data,b"测试数据" );printf
        ("数据 b = %s\n",.data)b;return
        0 ;}
    数据长度=100
    数据1
    数据10.000000
    数据测试数据
    
    
    #

    在这里所有的成员都可以正确输出,是因为同一时间只用到了一个变量

    18 typedef 18.1 typedef 的作用

    C语言提供typedef关键字,可以使用它为类型起一个新的名字

    includetypedef 
    
    unsigned int ; TEXTint
    main ()={
        TEXT a 11 ;printf
        ("参数为=%d\n",)a;return
        0 ;}
    参数为=11
    
    
    #

    也可以为用户自定义的数据类型取一个新的名字,比如结构体

    include# 
    includetypedef 
    
    unsigned int ; TEXTtypedef
    
    struct BOOKS char {
        [ a50];char
        [ b50];}
    ; Bookint
    
    main ()= {
        TEXT a 11 ;printf
        ("参数为=%d\n",) a;;
    
        Book bookstrcpy
    
        (.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
    
    
    #
    18.2 typedef和#define 的区别

    #define是一个C指令,用于为各种数据类型定义别名,与typedef类似,但是他有以下几种不同

    typedef仅限于为类型定义符号名称,#define不仅为类型定义别名,也可以为数值定义别名
    typedef为编译器解释执行,#define为预编译器进行处理

    defineTRUE 0 #
    defineFALSE 1 int
    
    main ()printf {
        ("数值为=%d\n",) TRUE;printf
        ("数值为=%d\n",) FALSE;return
        0 ;}
    数值为=0
    数值为=1
    
    
  • int getchar(void) 函数从屏幕读取下一个可用的字符,并把它返回为一个整数。这个函数在同一个时间内只会读取一个单一的字符。您可以在循环内使用这个方法,以便从屏幕上读取多个字符。
  • 19 输入和输出 19.1 getchar() & putchar() 函数
    includeint 
    
    main ()printf{
        ("请输入一个字符\n");int
    
        =  a getchar ();printf
    
        ("你的输入为\n");putchar
    
        ()a;printf
    
        ("\n");}
    请输入一个字符
    text
    你的输入为
    t
    
    
  • char *gets(char *s)函数从stdin读取一行到s指向的缓存区,直到一个终止符或一个EOF
  • 19.2 gets() & puts() 函数
    includeint 
    main ()char{
        [ a100];printf
    
        ("输入你的字符\n");gets
        ()a;printf
    
        ("你输入的字符为\n");puts
    
        ()a;return
        0 ;}
    输入你的字符
    text
    你输入的字符为
    text
    
    
  • int scanf(const char *format, …)函数从标准输入流 stdin 读取输入,并根据提供的 format 来浏览输入。
  • 19.3 scanf() 和 printf() 函数
    includeint 
    
    main ()char {
        [ a100];int
        ; bprintf
    
        ("输入文字\n");scanf
    
        ("%s%d",, a& )b;printf
    
        ("你输入的文字=%s,%d\n",,a)b;return
        0 ;}
    输入文字
    text 123
    你输入的文字=text,123
    
    
    #
    20 文件读写
    includeint 
    
    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,并写入俩行文字

    includeint 
    
    main ()* {
        FILE ;file1char
    
        [ a255];//打开一个文件,只读
        =
        file1 fopen ("/Users/Desktop/test.txt","r" );//读取文件,当遇到第一个空格或者换行符会停止读取
        fscanf
        (,file1"%s" ,) a;printf
        ("1= %s\n",) a;//读取字符串到缓冲区,遇到换行符或文件的末尾 EOF返回
    
        fgets
        (,a255 ,) file1;printf
        ("2= %s\n",) a;fgets
    
        (,a255 ,) file1;printf
        ("3= %s\n",) a;//关闭文件  
    
        fclose
        ()file1;}
    1= this
    2=  is a text
    3= this is a text aaaa
    
    

    读取刚才创建的文件

    指令
    21 预处理器

    C预处理器不是编译器的组成部分,他是编译过程中的一个单独步骤,他们会在编译器实际编译之前完成所需的预处理

    所有的预处理器都是以(#)开头的,他必须是第一个非空字符,为了增强可读性,预处理器应该从第一列开始,下面列出比较重要的预处理器

    #
    描述#define
    定义宏#undef
    取消定义的宏#include
    包含一个源文件代码#ifdef
    如果宏已经定义则返回真#ifndef
    如果宏没有定义则返回真#if
    如果给定条件为真则编译一下代码#else
    #if的替代方案#elseif
    如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码#endif
    结束一个#if。。#else 语句#error
    遇到标准错误是,输出错误消息#pragma
    使用标准化方法,向编译器发布特殊指令到编译器中去
    21.1 预编译器实例
    defineMAX 20 #
    

    这个指令表示,把所有的MAX定义为20,使用#define定义常量可以增加可读性

    include# 
    include"myheader.h" #
    

    第一个表示从系统库获取stdio.h库,并添加到源文件中,一个是从本地目录获取myheader.h,并添加到源文件中

    undefFILE  #
    defineFILE 99 #
    

    取消已经定义的FILE,并把它重新定义为99

    ifdefMESSAGE #
        defineMESSAGE "you wish" #
    endif
    

    这个表示只有MESSAGE未定义时,才定义MESSAGE为you wish

    21.2 预定义宏

    ANSI C中预定义宏,我们可以在编程中使用这些宏,但不可以改变他的值

    #
    描述1__DATE__
    当前日期,一个以 “MMM DD YYYY” 格式表示的字符常量。1__TIME__
    当前时间,一个以 “HH:MM:SS” 格式表示的字符常量。1__FILE__
    这会包含当前文件名,一个字符串常量。1__LINE__
    这会包含当前行号,一个十进制常量。1__STDC__
    当编译器以 ANSI 标准编译时,则定义为 1。

    接下来我们看下如何使用

    includeint 
    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 ;}
    =
    
    data31Mar 2020 =
    file.text27=c
    time15:31:49=
    line19=
    stdic1int
    
    21.3 参数化宏

    参数化宏可以模拟函数,例如下面是一个计算数字平方的方法

    square (int) xreturn {
       * x ; x}
    #
    

    可以用参数化宏来表示

    definesquare ()x( ()x*()x)#
    

    在使用参数化宏之前必须使用#define来定义,参数列表必须包含在圆括号内的,并且必须紧跟在宏名称之后,不允许有空格

    definesquare ()x( ()x*()x)int
    
    main ()printf {
    
        ("平方为=%d\n",square(5));return
    
        0 ;}
    平方为=25
    
    
    #
    21.4 预处理运算符 21.4.1 宏延续运算符(\)

    一个宏通常写在一个单行上,但是如果宏太长,一个单行容不下,则使用宏延续运算符(\)例如

    definemessage_for (,a)b\ printf
                ("and "#a#b"we are friend\n")#
    
    21.4.2 字符串常量化运算符(#)

    在宏定义中,需要把一个宏的参数转化为字符串常量时,则使用字符串常量化运算符(#)例如:

    definemessage_for (,a)b\ printf
                ("and "#a#b"we are friend\n")int
    
    main ()message_for {
    
        ("xiaoming","xiaohong");return
    
        0 ;}
    "xiaoming"and "xiaohong"we are friend
    
    
    #
    21.4.3 标记粘贴运算符(##)

    直接看代码比较

    defineadd ()nprintf ("token"# n" = %d\n" ,##token)nint
    
    main ()int {
        = token34 40;add
        (34);return
        0 ;}
    =
    
    token34 40 printf ("token34 = %d", token34);
    
    

    这个是怎么发生的,因为这个实际会从编译器中产出以下实际输出

    #
    21.4.4 defined()运算符

    预处理器defined运算符是在常量表达式中的,用来确定一个标识符是否已经#define定义过,如果定义过为真,如果没有定义过值为假

    if! defined()TEXT#
    defineTEXT "text\n" #
    endifint
    
    main ()printf {
        ()TEXT;return
        0 ;}
    text
    
    
    #
    22 头文件

    头文件是后缀名为.h的文件,包含了函数声明和宏定义,有俩种头文件:系统头文件和c程序员自己编写的头文件

    在C程序中建议把所有的常量,宏,系统全局变量,和函数原型写入头文件中

    22.1 引入头文件

    使用预处理命令#include 引入头文件,有俩种类型

    include# 
    

    这种形式用于引用系统头文件,他在系统目录搜索头文件

    include"file" #
    

    这种形式用于引用用户头文件,他在当前的文件目录搜索头文件

    22.2 只引用一次头文件

    如果一个头文件引用被引用俩次,编译器会处理俩次头文件的内容,这将产生错误,为了防止这种情况,标准的做法

    ifndefHEADER_FILE #
    defineHEADER_FILE #
    the entire header file file
    endif
  • perror()函数:他将显示你传入的文字后面跟一个冒号和来errno相关的文本信息
  • 如果没有定义HEADER_FILE则定义引入头文件,如果下次在引用因为HEADER_FILE已经定义则不会再次引入

    关于.h和.c的区别请看这篇文章C语言中.h和.c文件解析(很精彩)

    23 错误处理

    当C语言发生错误时,大多数的C会返回1或NULL,同时会设置一个错误代码errno,该错误代码是全局变量,表示执行函数期间发生了错误,可以在 errno.h头文件中找到各种错误代码
    所以程序员可以通过返回值来判断是否有错误,开发人员在初始化时把errno初始化为0,是一个良好的编程习惯,0表示没有错误

    23.1 errno、perror() 和 strerror()

    C语言提供了perror() 和 strerror()来显示errno相关的文本信息

    include# 
    include# 
    includeint 
    
    main ()* {
        FILE ;pfint
        ; errnum=
        pf fopen ("aaa.txt","rb" );if
    
        ( ==pfNULL)//stderr,输出到屏幕{
            printf
            ("错误号为%d\n",)errno;fprintf
            (stderr,"错误为%s\n",strerror()errno);perror
            ("perror显示错误");}
        return
        0 ;}
    错误号为2
    错误为No such file or directory
    perror显示错误: No such file or directory
    
    
    函数
    24 内存管理 24.1 内存 *** 作函数

    C语言为内存的分配提供了几个函数,在文件头中可以找到

    char
    描述void *calloc(int num, int size);
    在内存中动态的分配num个长度为size的连续空间,并将每一个字节初始化为0void free(void *address);
    该函数释放address所指向的内存块,释放的是动态分配的内存空间void *malloc(int num);
    在堆区分配一块指定大小的内存空间,用来储存数据,该内存空间在函数执行完之后不会初始化,他们的值是未知的void *realloc(void *address, int newsize);
    该函数重新分配内存,新内存分配到newsize大小

    注意:void* 表示未确定类型的指针,void*类型指针可以通过强制转换转换为任意其他类型的指针

    24.2 动态分配内存

    编程时如果你预先知道数组的大小,那么定义数组时会比较容易,比如一个可以容纳100个字符的数组

    [ name100];#
    

    但是如果不知道需要储存文本长度的,那就需要动态分配内存了

    include# 
    include# 
    includeint 
    
    main ()char {
        [ a100];char
        * ;bstrcpy
    
        (,a"固定数组100" );//动态分配内存
    
        =
        b ( char* )malloc (200* sizeof (char));//    b= calloc(200, sizeof(char));
    if
        ( ==b NULL )printf {
            ("分配内存失败");}
        else strcpy {
            (,b"动态分配内存成功" );}
        printf
        ("值为=%s\n",)b;//重新分配内存
        =
        b realloc (,b500 * sizeof (char));if
        ( ==b NULL )printf {
            ("分配内存失败");}
        else strcpy {
            (,b"重新分配内存成功" );}
        printf
    
        ("值为=%s\n",)b;//释放内存
    
        free
        ()b;return
        0 ;}
    值为=动态分配内存成功
    值为=重新分配内存成功
    
    
    =

    上面分配内存的代码也可以替换为

        bcalloc (200,sizeof (char));#
    
    25 命令行参数

    执行程序时可以从命令行传递参数给程序,这些值为命令行参数,命令行参数是通过main函数参数来处理的,其中,argc 是指传入参数的个数,argv[] 是一个指针数组,指向传递给程序的每个参数

    includeint 
    
    main (int, argcchar * [argv])if {
        ( ==argc 2 )printf {
            ("参数为 %s\n",[ argv1]);}
        else if ( 2argc > )printf {
            ("参数超过俩个\n");}
        else printf {
            ("只有一个参数\n");}
        return
        0 ;}
    L-96FCG8WP-1504:untitled renxiaohui$ gcc text30.c 
    L-96FCG8WP-1504:untitled renxiaohui$ a.out text
    参数为 text
    
    
    [+++]
    )
    File: /www/wwwroot/outofmemory.cn/tmp/route_read.php, Line: 126, InsideLink()
    File: /www/wwwroot/outofmemory.cn/tmp/index.inc.php, Line: 166, include(/www/wwwroot/outofmemory.cn/tmp/route_read.php)
    File: /www/wwwroot/outofmemory.cn/index.php, Line: 30, include(/www/wwwroot/outofmemory.cn/tmp/index.inc.php)
    NDK学习笔记<基础篇>C语言基础_C_内存溢出

    NDK学习笔记<基础篇>C语言基础

    NDK学习笔记<基础篇>C语言基础,第1张

    菜鸟教程之C语言教程
    Android JNI和NDK学习(基础篇):C语言基础

    前言

    C语言对于Android开发来说还是非常必要的,不管你是要阅读源码,还是想要学习NDK,音视频,性能优化等,都不可避免需要接触到C;
    而且C语言属于系统级的语言, *** 作系统内核都有C的身影,作为一名科班出身的程序员,在毕业7年以后决定重新捡起这门语言。

    一、C语言简介 1.1 C语言特点

    C语言最初适用于系统开发工作的,特别是组成 *** 作系统的程序,由于C语言产生的代码运行速度与汇编编写的代码运行速度几乎相同,所以采用C语言作为系统开发语言
    下面列举几个使用C的实例

    • *** 作系统
    • 文本编辑器
    • 打印机
    • 网络驱动器等
    1.2 C语言开发环境

    市面上能够进行C语言开发的编译器非常多,最经典的当然还是VC6.0,还有VS系列,以及MAC上的XCode,这里我使用的是CodeBlock;
    关于编译器的下载和安装,请自行解决

    1.3 Hello World
    #include 
    #include 
    
    int main()
    {
        /*
        我是一行注释
        */
        printf("Hello world!\n");
        return 0;
    }
    
    

    一个C程序一般包括以下几个部分:

    • 预处理器指令 : 程序的第一行#include 是预处理指令,告诉C编译器实际编译之前需要包括stdio.h文件
    • 函数 : 下一行int main()是主函数,程序从这里开始运行,printf()是程序中另一个可用的函数作用是在屏幕上显示Hello, World!
    • 变量
    • 语句&表达式
    • 注释: /* … */这就是一个注释
    • return 0;标识此函数结束,并返回0
    二、C语言入门 2.1 标识符

    标记符是用来标记变量,函数或者任何用户自定的变量名称,一个标识符以字母A—Z,a-z,或者_下划线开始,后面跟0个或多个字母,数字,下划线

    标识符中不允许出现标点字符,比如@%,标识符是区分大小写的

    2.2 关键字

    这些关键字不能作为常量名或者变量名,其他标识符的名称

    关键字说明
    continue结束当前循环,开始下一轮循环
    switch用于开关语句
    case开关语句分支
    default开关语句中的其他分支
    break跳出当前循环
    do循环语句的循环体
    while循环语句的循环条件
    if条件语句
    else条件语句否定分支与if一起使用
    for一种循环语句
    goto无条件跳转语句
    return子程序返回语句,可带参数可不带参数
    char声明字符变量或者返回值类型
    double声明双精度浮点类型对象或函数返回值类型
    float声明浮点型变量或返回值类型
    short声明短整形变量或者返回值类型
    int声明整形变量或者返回值类型
    long声明长整形变量或返回值类型
    unsigned声明无符号类型变量或返回值类型
    void声明函数无返回值或无参数,声明无类型指针
    enum声明枚举类型
    static声明静态变量
    void声明函数无返回值或无参数,声明无类型指针
    auto自动声明变量
    const定义常量,如果一个变量被const修饰,则它的值不能被改变
    extern声明变量或函数在其他文件或本文件的其他位置定义
    register声明寄存器变量
    signed声明有符号类型的变量
    sizeof计算数据类型或变量的长度
    struct声明结构体类型
    union声明共用体
    typedef给数据类型取别名
    volatile说明变量在执行过程中可以隐含的改变

    可以看到70%都是java中有的,学习起来并不是很难

    2.3 数据类型

    C中的数据类型可以分为以下几种

    2.3.1 整数类型
    类型存储大小值范围
    char1字节-128-127或0-255
    unsigned char1字节0-255
    signed char1字节-128-127
    int2或4字节-32,768 到 32,767 或 -2,147,483,648 到 2,147,483,647
    unsigned int2或4字节0 到 65,535 或 0 到 4,294,967,295
    short2字节-32,768 到 32,767
    unsigned short2字节0 到 65,535
    long4字节-2,147,483,648 到 2,147,483,647
    unsigned long4字节0 到 4,294,967,295

    各类型的储存大小,与系统位数有关,为了得到准确大小可以用sizeof来计算

    #include 
    #include 
    int main(){
        printf("int 存储大小 : %lu \n", sizeof(int));
        return 0 ;
    }
    

    int 存储大小 : 4

    2.3.2 浮点类型
    类型存储大小值范围精度
    float4字节1.2E-38 到 3.4E+386 位小数
    double8字节2.3E-308 到 1.7E+30815位小数
    long double16字节3.4E-4932 到 1.1E+493219位小数
    2.3.3 void类型
    类型描述
    函数返回为空C 中有各种函数都不返回值,或者您可以说它们返回空。不返回值的函数的返回类型为空。例如 void exit (int status);
    函数参数为空C 中有各种函数不接受任何参数。不带参数的函数可以接受一个 void。例如 int rand(void);
    指针指向void类型为void*指针代表对象的地址,而不是类型,例如,内存分配函数 void *malloc( size_t size ); 返回指向 void 的指针,可以转换为任何数据类型。
    2.4 变量

    变量是程序可 *** 作的储存区名称,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 常量

    常量是固定的值,在程序期间不会改变,这些固定的值又叫做字面量

    常量可以是任何基本数据类型,常量在值定义之后不会修改

    4.1 定义常量

    在C中有俩种定义常量的方式

    • 使用#define预处理器
    #define identifier value
    
    #include 
    #include 
    
    #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
    字符串,
    
    4.2 使用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;
    }
    
    5 储存类

    储存类定义C中变量,函数的范围和生命周期,这些说明符放在他们所修饰的类型之前,下面列出可用的储存类

    • auto
    • register
    • static
    • extern
    5.1 auto储存类

    auto储存类是所有局部变量默认的储存类

    {
       int mount;
       auto int month;
    }
    

    上面的两种写法都是一样的,auto只能用在函数内,即只能修饰局部变量

    5.2 register

    用于定义储存在寄存器中而不是RAM的局部变量,这意味着变量的最大大小是寄存器的大小

    {
       register int  miles;
    }
    

    寄存器只用于需要快速访问的变量,比如计数器

    5.3 static

    编译器在声明周期内保持保持局部变量的值,而不需要每次进入和离开作用域是创建和销毁,因此使用static修饰局部变量,可以函数调用间保持局部变量的值

    static也可以用于全局变量,当static修饰全局变量时,变量的作用域会限制在他的本文件中,也就是只有本文件才可以访问(普通的全局变量,使用extern外部声明后任何文件都可以访问)

    static函数:和上方的全局变量一样(非静态函数可以在另一个文件中直接引用,甚至不必使用extern声明)

    5.4 extern

    用于提供一个全局变量的引用,全局变量对所有的程序文件都可见

    当在A文件定义一个全局变量a,B文件想要使用A文件的变量a,可以在B文件使用extern关键字拿到a的引用

    第一个文件

    #include 
    
    int count =5;
    
    extern void add();
    
    int main() {
        add();
        return 0;
    }
    

    第二个文件

    #include 
    
    extern int count;
    
    void add(){
        printf("count=%d\n",count);
    }
    
    
    count=5
    
    6 运算符

    同java,不再记录

    7 判断

    同java

    8 循环

    同java

    9 函数 9.1 定义函数
    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;
    }
    
    9.2 函数声明

    函数的声明会告诉编译器,函数的名称和如何调用函数,函数的实际主体可以单独定义

    函数声明包括以下几个部分

    return_type function_name( parameter list );
    

    针对上方的函数我们可以声明

    int Max(int num1,int num2);
    

    在函数声明中,参数名称并不是很重要,可以省略掉

    int Max(int ,int);
    

    当你在一个源文件定义一个函数,在另一个文件调用这个函数时,函数声明是必要的,这种情况下,你需要在调用函数文件的顶部声明函数

    9.3 函数声明调用函数
    #include 
    
    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
    
    10 C的作用域规则 10.1 局部变量

    在某个函数块内声明的变量为局部变量,他只能被该函数或者该代码块内的函数语句使用,局部变量外部是不可知的

    #include 
     
    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 都是局部变量

    10.2 全局变量

    全局变量定义在函数的外部,通常是程序的顶部,全局变量在整个程序的生命周期内都是有效的,在任意的函数内部可以访问全局变量。
    全局变量可以被任意函数访问,也就是说全局变量在声明后在整个程序中都是可用的
    局部变量和全局变量名称可以相同,但是在函数内,如果俩个名字相,会使用局部变量,不会使用全局变量

    #include 
    
    //全局变量
    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
    
    10.3 全局变量与局部变量的区别
    • 全局变量保存在内存的全局储存区,占用静态的储存单元
    • 局部变量保存在栈中,只有在函数被调用时,才动态的为变量分配储存单元
    11 数组 11.1 声明数组
    type arrayName [ arraySize ];
    

    这是一个一维数组,arraySize必须是大于0的常量,type是任意有效的c数据类型,声明一个含有10个double数据的nums数组

    double nums [10];
    
    11.2 初始化数组

    初始化固定数量的数组

    int nums [3] = {10,2,3}
    

    初始化数量不固定的数组

    int nums []={1,2,3,4,5}
    

    为某个数组赋值

    nums[3] = 6;
    

    访问数组元素

    int a = num[3];
    

    实例

    #include 
    
    
    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
    
    12 枚举

    枚举是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,以此类推

    12.1 枚举变量的定义

    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 
    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语言规范是没有办法遍历枚举的

    12.2 枚举在switch中使用
    #include 
    #include 
    
    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
    你喜欢红色
    
    13 指针

    每一个变量都有一个内存位置,每一个内存位置都定义了可使用&符号访问地址,他表示在内存中的一个地址

    #include 
    
    int main(){
    
        int a ;
    
        int b[10];
    
        printf("a 的内存地址=%p\n",&a);
        printf("b 的内存地址=%p\n",&b);
    
        return 0;
    }
    
    a 的内存地址=0x7ffee5e086c8
    b 的内存地址=0x7ffee5e086d0
    
    13.1 什么是指针

    指针是一个变量,其值为另一个变量的内存地址,使用指针之前需要对其进行声明

    type *var-name;
    

    type是指针的类型,他必须是一个有效的C数据类型,var-name是指针的名称,*用来表示这个变量是指针。

    int *aa;//一个整形指针
    double  *bb;//一个double类型的指针
    
    13.2 如何使用指针

    使用指针会频繁的进行如下几个 *** 作,定义一个指针变量,把变量地址赋值给指针,访问指针变量中的可用地址

    #include 
    
    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
    
    13.3 C中的NULL指针

    变量声明的时候,如果没有明确的地址可以赋值,为指针赋值一个NULL是一个良好的习惯,赋值为NULL被称为空指针

    int main(){
        int *var = NULL;
    
        printf("var的地址为=%p\n",var);
    
        return 0;
    }
    
    13.4 指针型指针
    #include 
    
    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
    
    13.5 传递数组给函数,函数返回数组
    #include 
    
    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
    
    13.6 指针运算

    C指针用数字表示地址,因此可以进行算术运算,++,–,+,-等,假如prt是一个int类型的地址1000,那么执行prt++,prt将指向1004,即当前位置移动4个字节,假如prt是一个char类型的地址1000,那么执行prt++,prt将指向1001,这个跟类型也是相关的

    • 指针的每一次递增,他实际上会指向下一个元素的储存单元
    • 指针的每一次递减,他实际上指向上一个元素的储存单元
    • 指针在递增和递减跳跃的字节数,取决于指针所指向变量的数据类型的长度,比如int就是4个字节

    递增一个指针

    #include 
    
    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
    
    13.7 指针数组

    有一种情况,我们数组内可以存储内存地址值

    #include 
    
    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
    
    13.7 传递指针给函数
    #include 
    #include 
    
    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
    
    14 函数指针 14.1 指向函数的指针

    函数指针是指向函数的指针变量,函数指针可以向普通函数一样,传递参数,调用函数

    函数返回值类型 (* 指针变量名) (函数参数列表);

    #include 
    
    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;
    }
    
    14.2 将指针函数当做参数传递给函数
    #include 
    #include 
    
    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
    
    15 字符串

    C语言定义字符串Hello,两种形式,字符串和字符数组的区别:最后一位是否是空字符

    #include 
    
    int main(){
        char hello[6]= {'H','e','l','l','o','}';char
        [ hello1]="hello" ;char
        * =hello2"hello";printf
        ("测试=%s\n",)hello;printf
        ("测试=%s\n",)hello1;printf
        ("测试=%s\n",)hello2;return
        0 ;}
    测试=Hello
    测试=hello
    测试=hello
    
    
    struct
    16 结构体 16.1 结构体的定义

    定义一个结构体

    tag - { 
        member-list
        member-list 
        member.list  
        ..}
    - variable;list 
  • tag:结构体标签
    • member-list:结构体数据项
    • variable-list:结构体变量
    • struct
    Book int {
        ; book_id char
        [ title50];char
        [ author50];char
        [ subject50];}
    ; book//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c
    

    在一般情况下,tag、member-list、variable-list 这 3 部分至少要出现 2 个。以下为实例:

    //同时又声明了结构体变量s1
    //这个结构体并没有标明其标签
    struct
    int 
    {
        ; achar
        ; bdouble
        ; c}
    ; s1//此声明声明了拥有3个成员的结构体,分别为整型的a,字符型的b和双精度的c
     
    //结构体的标签被命名为SIMPLE,没有声明变量
    struct
    SIMPLE int
    {
        ; achar
        ; bdouble
        ; c}
    ;//用SIMPLE标签的结构体,另外声明了变量t1、t2、t3
    struct
    SIMPLE , t1[ t220],* ;t3//也可以用typedef创建新类型
     
    typedef
    struct int
    {
        ; achar
        ; bdouble
        ; c} 
    ; Simple2//现在可以用Simple2作为类型声明新的结构体变量
    ,
    Simple2 u1[ u220],* ;u3//结构体包含其他结构体
    
    16.2 结构体中可以含有其他结构体和指针
    struct
    AA int{
        ; astruct
        Book ; b}
    ;//结构体包含自己的指针
    struct
    BB int{
        ; bstruct
        BB * }nextbb
    ;struct
    
    16.3 结构体的初始化
    Book int {
        ; book_id char
        [ title50];char
        [ author50];char
        [ subject50];}
    = book1 {,"c语言","小明","bb"};title : c语言
    author: 小明
    subject: bb
    book_id: 1
    
    
    int
    16.4 访问结构体的成员

    访问结构体的成员可以用.符号,比如上方的book.title;

    main ()struct{
        Book ; book1strcpy
        (.book1,title"学习书");strcpy
        (.book1,author"小红");strcpy
        (.book1,subject"111");.
        book1=book_id222;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
    16.5 结构体作为函数的参数
    
    printstuct (structBook ) book;int
    
    main ()struct{
    
        Book ; book1strcpy
    
        (.book1,title"学习书");strcpy
        (.book1,author"小红");strcpy
        (.book1,subject"111");.
    
        book1=book_id222;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 (structBook ) bookprintf{
        ("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: 小红
    subject111 :
    book_id222 :
    Book title : 学习书
    Book author : 小红
    Book subject 111 :
    Book book_id 222 struct
    
    16.6 指向结构体的指针

    定义,赋值,调用

    Books * ;struct_pointer=
    
    struct_pointer & ;Book1;
    
    struct_pointer->titleint
    
    main ()struct {
    
        Book ; book1strcpy
    
        (.book1,title"学习书" );strcpy
        (.book1,author"小红" );strcpy
        (.book1,subject"111" );.
    
        book1=book_id 222 ;printstuct1
        (&)book1;return
        0 ;}
    void
    
    printstuct1 (structBook * )bookprintf {
        ("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
    
    17 共用体

    共用体是一个特殊的数据类型,允许在相同的储存位置,储存不同的数据类型,可以定义一个带有多个成员的共用体,但是任何时候只能一个成员带值

    17.1 定义共用体

    用union定义共用体

    [ union] tag;
    {
       member definition;
       member definition.
       ..;
       member definition}
    [ unionone or more ] variables;#
    

    union tag是可选的,每个member definition,都是标准的变量定义,如int i ; char b等,在分号之前可以定义一个或多个共用体变量是可选的

    定义一个成员有int,float,char[]的共用体

    
    include# 
    includeunion 
    
    int Data {
        ; achar
        [ b100];float
        ; c}
    ;int
    
    main ()union {
    
        ; Data dataprintf
        ("数据长度=%lu\n",sizeof ()data);.
    
        data=a 1 ;.
        data=c 10.00 ;strcpy
        (.data,b"测试数据" );printf
        
        ("数据 a = %d\n",.data)a;printf
        ("数据 c = %f\n",.data)c;printf
        ("数据 b = %s\n",.data)b;return
        0 ;}
    数据长度=100
    数据 a = -393497114
    数据 c = -5278115000342806695772160.000000
    数据 b = 测试数据
    
    
    
    #

    可以看到数据a,c成员的值有损坏,是因为最后赋值的变量占用了的内存位置,这也是b变量可以正确输出的原因。

    我们同一时间只能使用一个变量

    
    include# 
    includeunion 
    
    int Data {
        ; achar
        [ b100];float
        ; c}
    ;int
    
    main ()union {
    
        ; Data dataprintf
        ("数据长度=%lu\n",sizeof ()data);.
    
        data=a 1 ;printf
        ("数据 a = %d\n",.data)a;.
    
        data=c 10.00 ;printf
        ("数据 c = %f\n",.data)c;strcpy
    
        (.data,b"测试数据" );printf
        ("数据 b = %s\n",.data)b;return
        0 ;}
    数据长度=100
    数据1
    数据10.000000
    数据测试数据
    
    
    #

    在这里所有的成员都可以正确输出,是因为同一时间只用到了一个变量

    18 typedef 18.1 typedef 的作用

    C语言提供typedef关键字,可以使用它为类型起一个新的名字

    includetypedef 
    
    unsigned int ; TEXTint
    main ()={
        TEXT a 11 ;printf
        ("参数为=%d\n",)a;return
        0 ;}
    参数为=11
    
    
    #

    也可以为用户自定义的数据类型取一个新的名字,比如结构体

    include# 
    includetypedef 
    
    unsigned int ; TEXTtypedef
    
    struct BOOKS char {
        [ a50];char
        [ b50];}
    ; Bookint
    
    main ()= {
        TEXT a 11 ;printf
        ("参数为=%d\n",) a;;
    
        Book bookstrcpy
    
        (.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
    
    
    #
    18.2 typedef和#define 的区别

    #define是一个C指令,用于为各种数据类型定义别名,与typedef类似,但是他有以下几种不同

    typedef仅限于为类型定义符号名称,#define不仅为类型定义别名,也可以为数值定义别名
    typedef为编译器解释执行,#define为预编译器进行处理

    defineTRUE 0 #
    defineFALSE 1 int
    
    main ()printf {
        ("数值为=%d\n",) TRUE;printf
        ("数值为=%d\n",) FALSE;return
        0 ;}
    数值为=0
    数值为=1
    
    
  • int getchar(void) 函数从屏幕读取下一个可用的字符,并把它返回为一个整数。这个函数在同一个时间内只会读取一个单一的字符。您可以在循环内使用这个方法,以便从屏幕上读取多个字符。
  • 19 输入和输出 19.1 getchar() & putchar() 函数
    • int putchar(int c) 函数把字符输出到屏幕上,并返回相同的字符。这个函数在同一个时间内只会输出一个单一的字符。您可以在循环内使用这个方法,以便在屏幕上输出多个字符。
    • #
    includeint 
    
    main ()printf{
        ("请输入一个字符\n");int
    
        =  a getchar ();printf
    
        ("你的输入为\n");putchar
    
        ()a;printf
    
        ("\n");}
    请输入一个字符
    text
    你的输入为
    t
    
    
  • char *gets(char *s)函数从stdin读取一行到s指向的缓存区,直到一个终止符或一个EOF
  • 19.2 gets() & puts() 函数
    • int **puts(const char *s)函数吧一个字符串s和一个尾随的换行符写入stdout
    • #
    includeint 
    main ()char{
        [ a100];printf
    
        ("输入你的字符\n");gets
        ()a;printf
    
        ("你输入的字符为\n");puts
    
        ()a;return
        0 ;}
    输入你的字符
    text
    你输入的字符为
    text
    
    
  • int scanf(const char *format, …)函数从标准输入流 stdin 读取输入,并根据提供的 format 来浏览输入。
  • 19.3 scanf() 和 printf() 函数
      #
    • int printf(const char *format, …)函数把输出写入到标准输出流 stdout ,并根据提供的格式产生输出。
      format 是一个简单的常量字符串,但是你可以定义%s,%d,%c,%f来读取字符串,数字,字符,浮点数
    includeint 
    
    main ()char {
        [ a100];int
        ; bprintf
    
        ("输入文字\n");scanf
    
        ("%s%d",, a& )b;printf
    
        ("你输入的文字=%s,%d\n",,a)b;return
        0 ;}
    输入文字
    text 123
    你输入的文字=text,123
    
    
    #
    20 文件读写
    includeint 
    
    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,并写入俩行文字

    includeint 
    
    main ()* {
        FILE ;file1char
    
        [ a255];//打开一个文件,只读
        =
        file1 fopen ("/Users/Desktop/test.txt","r" );//读取文件,当遇到第一个空格或者换行符会停止读取
        fscanf
        (,file1"%s" ,) a;printf
        ("1= %s\n",) a;//读取字符串到缓冲区,遇到换行符或文件的末尾 EOF返回
    
        fgets
        (,a255 ,) file1;printf
        ("2= %s\n",) a;fgets
    
        (,a255 ,) file1;printf
        ("3= %s\n",) a;//关闭文件  
    
        fclose
        ()file1;}
    1= this
    2=  is a text
    3= this is a text aaaa
    
    

    读取刚才创建的文件

    指令
    21 预处理器

    C预处理器不是编译器的组成部分,他是编译过程中的一个单独步骤,他们会在编译器实际编译之前完成所需的预处理

    所有的预处理器都是以(#)开头的,他必须是第一个非空字符,为了增强可读性,预处理器应该从第一列开始,下面列出比较重要的预处理器

    #
    描述#define
    定义宏#undef
    取消定义的宏#include
    包含一个源文件代码#ifdef
    如果宏已经定义则返回真#ifndef
    如果宏没有定义则返回真#if
    如果给定条件为真则编译一下代码#else
    #if的替代方案#elseif
    如果前面的 #if 给定条件不为真,当前条件为真,则编译下面代码#endif
    结束一个#if。。#else 语句#error
    遇到标准错误是,输出错误消息#pragma
    使用标准化方法,向编译器发布特殊指令到编译器中去
    21.1 预编译器实例
    defineMAX 20 #
    

    这个指令表示,把所有的MAX定义为20,使用#define定义常量可以增加可读性

    include# 
    include"myheader.h" #
    

    第一个表示从系统库获取stdio.h库,并添加到源文件中,一个是从本地目录获取myheader.h,并添加到源文件中

    undefFILE  #
    defineFILE 99 #
    

    取消已经定义的FILE,并把它重新定义为99

    ifdefMESSAGE #
        defineMESSAGE "you wish" #
    endif
    

    这个表示只有MESSAGE未定义时,才定义MESSAGE为you wish

    21.2 预定义宏

    ANSI C中预定义宏,我们可以在编程中使用这些宏,但不可以改变他的值

    #
    描述1__DATE__
    当前日期,一个以 “MMM DD YYYY” 格式表示的字符常量。1__TIME__
    当前时间,一个以 “HH:MM:SS” 格式表示的字符常量。1__FILE__
    这会包含当前文件名,一个字符串常量。1__LINE__
    这会包含当前行号,一个十进制常量。1__STDC__
    当编译器以 ANSI 标准编译时,则定义为 1。

    接下来我们看下如何使用

    includeint 
    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 ;}
    =
    
    data31Mar 2020 =
    file.text27=c
    time15:31:49=
    line19=
    stdic1int
    
    21.3 参数化宏

    参数化宏可以模拟函数,例如下面是一个计算数字平方的方法

    square (int) xreturn {
       * x ; x}
    #
    

    可以用参数化宏来表示

    definesquare ()x( ()x*()x)#
    

    在使用参数化宏之前必须使用#define来定义,参数列表必须包含在圆括号内的,并且必须紧跟在宏名称之后,不允许有空格

    definesquare ()x( ()x*()x)int
    
    main ()printf {
    
        ("平方为=%d\n",square(5));return
    
        0 ;}
    平方为=25
    
    
    #
    21.4 预处理运算符 21.4.1 宏延续运算符(\)

    一个宏通常写在一个单行上,但是如果宏太长,一个单行容不下,则使用宏延续运算符(\)例如

    definemessage_for (,a)b\ printf
                ("and "#a#b"we are friend\n")#
    
    21.4.2 字符串常量化运算符(#)

    在宏定义中,需要把一个宏的参数转化为字符串常量时,则使用字符串常量化运算符(#)例如:

    definemessage_for (,a)b\ printf
                ("and "#a#b"we are friend\n")int
    
    main ()message_for {
    
        ("xiaoming","xiaohong");return
    
        0 ;}
    "xiaoming"and "xiaohong"we are friend
    
    
    #
    21.4.3 标记粘贴运算符(##)

    直接看代码比较

    defineadd ()nprintf ("token"# n" = %d\n" ,##token)nint
    
    main ()int {
        = token34 40;add
        (34);return
        0 ;}
    =
    
    token34 40 printf ("token34 = %d", token34);
    
    

    这个是怎么发生的,因为这个实际会从编译器中产出以下实际输出

    #
    21.4.4 defined()运算符

    预处理器defined运算符是在常量表达式中的,用来确定一个标识符是否已经#define定义过,如果定义过为真,如果没有定义过值为假

    if! defined()TEXT#
    defineTEXT "text\n" #
    endifint
    
    main ()printf {
        ()TEXT;return
        0 ;}
    text
    
    
    #
    22 头文件

    头文件是后缀名为.h的文件,包含了函数声明和宏定义,有俩种头文件:系统头文件和c程序员自己编写的头文件

    在C程序中建议把所有的常量,宏,系统全局变量,和函数原型写入头文件中

    22.1 引入头文件

    使用预处理命令#include 引入头文件,有俩种类型

    include# 
    

    这种形式用于引用系统头文件,他在系统目录搜索头文件

    include"file" #
    

    这种形式用于引用用户头文件,他在当前的文件目录搜索头文件

    22.2 只引用一次头文件

    如果一个头文件引用被引用俩次,编译器会处理俩次头文件的内容,这将产生错误,为了防止这种情况,标准的做法

    ifndefHEADER_FILE #
    defineHEADER_FILE #
    the entire header file file
    endif
  • perror()函数:他将显示你传入的文字后面跟一个冒号和来errno相关的文本信息
  • 如果没有定义HEADER_FILE则定义引入头文件,如果下次在引用因为HEADER_FILE已经定义则不会再次引入

    关于.h和.c的区别请看这篇文章C语言中.h和.c文件解析(很精彩)

    23 错误处理

    当C语言发生错误时,大多数的C会返回1或NULL,同时会设置一个错误代码errno,该错误代码是全局变量,表示执行函数期间发生了错误,可以在 errno.h头文件中找到各种错误代码
    所以程序员可以通过返回值来判断是否有错误,开发人员在初始化时把errno初始化为0,是一个良好的编程习惯,0表示没有错误

    23.1 errno、perror() 和 strerror()

    C语言提供了perror() 和 strerror()来显示errno相关的文本信息

    • strerror()函数:返回一个指针,指向当前 errno 值的文本表示形式。
    • #
    include# 
    include# 
    includeint 
    
    main ()* {
        FILE ;pfint
        ; errnum=
        pf fopen ("aaa.txt","rb" );if
    
        ( ==pfNULL)//stderr,输出到屏幕{
            printf
            ("错误号为%d\n",)errno;fprintf
            (stderr,"错误为%s\n",strerror()errno);perror
            ("perror显示错误");}
        return
        0 ;}
    错误号为2
    错误为No such file or directory
    perror显示错误: No such file or directory
    
    
    函数
    24 内存管理 24.1 内存 *** 作函数

    C语言为内存的分配提供了几个函数,在文件头中可以找到

    char
    描述void *calloc(int num, int size);
    在内存中动态的分配num个长度为size的连续空间,并将每一个字节初始化为0void free(void *address);
    该函数释放address所指向的内存块,释放的是动态分配的内存空间void *malloc(int num);
    在堆区分配一块指定大小的内存空间,用来储存数据,该内存空间在函数执行完之后不会初始化,他们的值是未知的void *realloc(void *address, int newsize);
    该函数重新分配内存,新内存分配到newsize大小

    注意:void* 表示未确定类型的指针,void*类型指针可以通过强制转换转换为任意其他类型的指针

    24.2 动态分配内存

    编程时如果你预先知道数组的大小,那么定义数组时会比较容易,比如一个可以容纳100个字符的数组

    [ name100];#
    

    但是如果不知道需要储存文本长度的,那就需要动态分配内存了

    include# 
    include# 
    includeint 
    
    main ()char {
        [ a100];char
        * ;bstrcpy
    
        (,a"固定数组100" );//动态分配内存
    
        =
        b ( char* )malloc (200* sizeof (char));//    b= calloc(200, sizeof(char));
    if
        ( ==b NULL )printf {
            ("分配内存失败");}
        else strcpy {
            (,b"动态分配内存成功" );}
        printf
        ("值为=%s\n",)b;//重新分配内存
        =
        b realloc (,b500 * sizeof (char));if
        ( ==b NULL )printf {
            ("分配内存失败");}
        else strcpy {
            (,b"重新分配内存成功" );}
        printf
    
        ("值为=%s\n",)b;//释放内存
    
        free
        ()b;return
        0 ;}
    值为=动态分配内存成功
    值为=重新分配内存成功
    
    
    =

    上面分配内存的代码也可以替换为

        bcalloc (200,sizeof (char));#
    
    25 命令行参数

    执行程序时可以从命令行传递参数给程序,这些值为命令行参数,命令行参数是通过main函数参数来处理的,其中,argc 是指传入参数的个数,argv[] 是一个指针数组,指向传递给程序的每个参数

    includeint 
    
    main (int, argcchar * [argv])if {
        ( ==argc 2 )printf {
            ("参数为 %s\n",[ argv1]);}
        else if ( 2argc > )printf {
            ("参数超过俩个\n");}
        else printf {
            ("只有一个参数\n");}
        return
        0 ;}
    L-96FCG8WP-1504:untitled renxiaohui$ gcc text30.c 
    L-96FCG8WP-1504:untitled renxiaohui$ a.out text
    参数为 text
    
    
    					
    										
    
    
    					

    欢迎分享,转载请注明来源:内存溢出

    原文地址: https://outofmemory.cn/langs/1329813.html

    (0)
    打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
    上一篇 2022-06-12
    下一篇 2022-06-12

    发表评论

    登录后才能评论

    评论列表(0条)

    保存