嵌入式linux学习笔记:06_C语言_函数及数据组织

一、字符串函数

1、字符串在程序中如何保存使用?
char A[10] = “hello”; -> 将常量区"hello"拷贝到数组A内存空间。

char *p = “hello”; -> 将常量区"hello"首元素的地址保存在指针变量p中。

2、在linux下,有些关于字符串函数提供用户使用。
1)计算字符串实际长度 ->strlen()
2)比较两个字符串是否匹配 -> strcmp()
3)拷贝字符串到某段内存中 -> strcpy()
4)追加字符串 ->strcat()

1、计算字符串实际长度 strlen( )-> 不包含’\0’在内

Ubuntu:man 3 strlen

NAME -> 函数的功能

strlen - calculate the length of a string -> 校对字符串长度

#include <string.h> -> 头文件

size_t strlen(const char *s); -> 函数原型

s: 需要计算的字符串的首元素的地址

返回值:字符的个数。

例子:

#include <stdio.h>
#include <string.h>
int main()
{
       char A[100] = "helloworld";

       char *p = "helloworld";

       printf("sizeof(A) = %d\n",sizeof(A));//100

       printf("sizeof(p) = %d\n",sizeof(p));//4

       printf("strlen(A) = %d\n",strlen(A));//10

       printf("strlen(p) = %d\n",strlen(p));//10

       return 0;
}

结论: sizeof()计算空间大小,strlen()计算字符串的长度。

2、比较两个字符串是否匹配 -> strcmp()

1、与整型变量是否相等比较
int a = 200;
int b = 100;
if(a == b) -> 整型数据使用"=="来进行判断

2、比较两个字符串能不能用"=="?
char A[100] = “helloworld”;

char *p = “helloworld”;

if(A == p) -> 不可以,只能判断两个地址是否相等,这样对比是没有任何意义,只有对比内容才有意义。

3、 如何比较两个字符串? -> 只能使用strcmp() -> man 3 strcmp

NAME
strcmp, strncmp - compare two strings 比较两个字符串

#include <string.h>

int strcmp(const char *s1, const char *s2);

int strncmp(const char *s1, const char *s2, size_t n);

s1:需要进行比较的字符串1的地址

s2:需要进行比较的字符串2的地址

n: 只匹配前n个字节

返回值:

s1s2匹配 -> 0

s1s2不匹配 -> 非0

4、例子:

#include <stdio.h>
#include <string.h>
int main()
{
     int ret;
     char A[100] = "helloworld";
    char *p = "hello";
       ret = strncmp(A,p);
       if(ret == 0){
              printf("match!\n");
       }
       else{
              printf("no match!\n");
       }
	return 0;
}

练习:有一张银行卡,密码是gec123456,请求用户输入密码,如果密码正确,则输出密码的长度,如果密码错误,则重新输入!如果输入的次数超过5次,则程序直接退出。

#include <stdio.h
#include <string.h>
int main(int argc,char *argv[]) 
{
       char passwd[20] = "gec123456"; //银行卡密码
       char input_passwd[20] = {0}; //清空数组
       char *p = input_passwd;
       int count = 0;
       while(count < 5)
       {
              scanf("%s",p);
              if(strcmp(passwd,p) == 0)
              {
                    printf("passwd length = %d\n",strlen(passwd));
                     break;
              }
              else{
                     count++;
                     continue;
              }
       }      
       return 0;
}

3、拷贝字符串到某段内存中 -> strcpy()

char A[10] = “hello”;

char A[10] = {“hello”};

char A[10] = {‘h’,‘e’,‘l’,‘l’,‘o’};

char A[10]; -> 如果定义没有初始化,则以后都不能整体初始化。

A[0] = ‘h’;

strcpy() -> 如果定义没有初始化,则以后还能使用strcpy()整体初始化。 -> man 3 strcpy

char A[10];

A = “hello” //错误

strcpy(A,“hello”); //正确

NAME
strcpy, strncpy - copy a string

SYNOPSIS

#include <string.h>

char *strcpy(char *dest, const char *src);

char *strncpy(char *dest, const char *src, size_t n);

dest:需要把字符串拷贝到的空间的起始地址,这个空间必须足够大。
src:需要拷贝的字符串的地址。
n: 只拷贝src前n个字节
返回值:

成功: 返回指向dest内存空间的地址

失败: NULL

#include <stdio.h>
#include <string.h>
int main()
{
       char A[11] = "helloworld";
       char *p = "abc";
       int i;
       for(i=0;i<11;i++)
       {
              printf("A[%d] = %c\n",i,A[i]);
       }
       strcpy(A,p);
       for(i=0;i<11;i++)
       {
              printf("A[%d] = %c\n",i,A[i]);
       }
       printf("A = %s\n",A);
       return 0;
}

4、字符串函数 -> 追加字符串 strcat()

字符串函数 -> 追加字符串strcat()-> man 3 strcat

使用格式:
#include <string.h>

char *strcat(char *dest, const char *src);

char *strncat(char *dest, const char *src, size_t n);

src:需要追加到另一个字符串默认的字符串的地址。 -> 拷贝之后会覆盖dest的\0。
dest:被追加的字符串的空间,空间必须足够大。
返回值:指向dest区域的首元素的地址。
验证: overwriting the terminating null byte (’\0’) at the end of dest, and then adds a terminating null byte.

src覆盖dest的\0,并且会在拼接之后在字符串的默认添加一个\0。

#include <stdio.h>
#include <string.h>
int main()
{
       /* 可以 */
       char A[20] = "hello";
       strcat(A,"world");
       /* 可以 */
       char A[20] = "hello";
       char *p = A;
       strcat(p,"world");
       /* 不可以 */
       char *p = "hello";
       strcat(p,"world");
       printf("p = %s\n",p); //helloworld
       return 0;
}

总结:学习过4个字符串函数的使用场景

  1. strlen() -> 确定某些数据的字节数 -> 文件IO/系统编程/网络编程 write()/send()
  2. strcmp() -> 检索特征数据 -> 链表/网络编程 -> 对比特征值/协议是否一致
  3. strcpy() -> 用于初始化字符数组
char A[10]="helloworld";  对
char A[10];
A = "helloworld";   不对
char A[10];
strcpy(A,"hello");
  1. strcat -> 拼接字符串,仅限追加功能 -> 后期使用sprintf() 替代 strcat()

5、 数组清零方式

1、定义数组的同时初始化0
例子: char str[50] = {0};

特点:只能初始化(清零)一次。

2、清空某段内存空间。 -> bzero() -> man 3 bzero
bzero - write zero-valued bytes -> 函数的功能

#include <strings.h> -> 头文件

void bzero(void *s, size_t n); -> 函数原型

s:需要清零的内存的地址
n:需要清零的字节数
返回值:无
特点:多次调用,多次清零。

#include <stdio.h>
#include <strings.h>
int main()
{
       char A[10];
       int i;
       for(i=0;i<10;i++)
       {
              printf("A[%d] = %c\n",i,A[i]);
       }
       bzero(A,sizeof(A));
       for(i=0;i<10;i++)
       {
              printf("A[%d] = %c\n",i,A[i]);
       }
       return 0;
}

二、堆空间

1、堆空间的特点:主动申请,主动释放

2、如何申请堆空间? -> malloc() -> man 3 malloc

#include <stdlib.h>

void *malloc(size_t size);

size:需要申请的字节数
The memory is not initialized. -> 内存没有被初始化过。

返回值:
成功: 指向堆空间的起始地址

失败: NULL

例子:

void *pa = malloc(4) -> 在堆空间申请4个字节,然后在栈空间申请一个指针变量pa指向堆空间。

int *p = pa; -> 把堆空间的pa赋值赋值给指针变量p

int pa; -> 在栈空间申请4个字节

int *p = &pa; -> 把栈空间pa变量的地址赋值给指针变量p

例题1: 申请了堆空间,堆空间的值会是什么?

#include <stdio.h>
#include <stdlib.h>
int main()
{
       int A[3];

       printf("A = %p\n",A);

       int *p = malloc(sizeof(int)*3);  // int A[3];

       printf("p = %p\n",p);

       printf("p[0] = %d\n",p[0]); //0

       printf("p[1] = %d\n",p[1]); //0

       printf("p[2] = %d\n",p[2]); //0

       free(p);

       return 0;
}

3、如何释放空间? -> free() -> man 3 free

#include <stdlib.h>

void free(void *ptr);

ptr:需要释放堆空间的地址
返回值:无
一般做法:free掉指向堆区指针之后,会让指针指向NULL。
free§;
p = NULL;

三、堆空间与栈空间在内存中容易出错的点

下列代码是否正确?如果正确,请指出代码的含义,如果出错,则说明错误的原因。

1、栈区

char A[10] = “hello”; //把常量区的hello拷贝到变量A数组中

strcpy(A,“world”); //把常量区的world拷贝到变量A数组中

============================
char *p = “hello”; //把常量区的hello的首元素的地址赋值给变量p

strcpy(p,“hello”); //段错误,因为p只能存放地址,不能存放字符串。

============================

char A[10];

strcpy(A,“hello”); //可以

============================

char *p;

strcpy(p,“hello”); //段错误,因为p只能存放地址,不能存放字符串。

============================

char A[10]; //在栈区中申请10个字节,使用变量A间接访问这个数组

char *p = A; //在栈区申请4个字节,使用变量p间接访问这片内存,将数组的A的首元素的地址赋值给p

strcpy(p,“hello”); //将常量区的hello拷贝到p指向的栈区空间

============================

2、堆区

char *p = malloc(sizeof(char)*10); //在堆区中申请10个字节,使用p间接访问这片内存空间。

p = “hello”; //给p赋值了常量区hello的地址,就是说现在p不是指向堆,而是指向常量区。

============================

char *p = malloc(sizeof(char)*10); //在堆区中申请10个字节,使用p间接访问这片内存空间。

strcpy(p,“hello”); //可以,把常量区的hello拷贝到堆区!

四、结构体

1、什么是结构体?

将多个不同类型的变量加入到一个集合中,这个集合就称之为结构体

由于结构体中存在不同类型的变量,所以每一个变量都需要用户自己定义。

可以在结构体中定义:
基本数据类型: char short int long float double

非基本数据类型:int A[3]int*pchar B[2][3]char *pxchar pa[5]int(*pfun)(int)

不可以在结构体中定义:函数

2、 如何定义结构体?

关键词:struct
模型:

struct 结构体名字{
       /* 结构体的组成变量 */
       例子:
       int a;
       char b;
       int A[3];
       int *p;
};   -> 后面记住有一个分号,不然报错。

其实结构体就是一种新的数据类型。
例子:

struct data{
       int a;
       char b;   
}; 

在这里只是先说: “struct data” 这种新类型由 {int a;char b;}组成。

3、如何定义结构体的变量?

struct mydata{
       char name[20];
       int age;
};

struct mydata -> 新的数据类型

定义变量公式: 数据类型 + 变量名;

定义整型变量 int a;
定义结构体变量 struct mydata A;

4、结构体的指针如何定义?

定义结构体变量: struct mydata A;
1)先写一个*

2)在*后面写一个变量的名字 *p

3)确认指针指向的内容是什么。struct mydata A;

4)将指向的内容的变量名去掉struct mydata

5)将第4步的结果写在第2步的前面 struct mydata *p;

结果:struct mydata *p;
变量名: p
数据类型: struct mydata *

5、结构体的变量与指针如何访问成员?

1)设计结构体模型

struct mydata{
       char name[20];
       int age;
};

2)定义一个结构体的变量
struct mydata gec;

3)结构体变量使用"."来对成员进行访问。
公式: 结构体变量名.成员名字

例子:

strcpy(gec.name,"ggy");

gec.age = 10;

4)结构体指针使用"->"访问成员。
公式: 结构体指针变量名->成员名字

例子:

strcpy(p->name,"ggy");

p->age = 10;

6、结构体初始化值

1)先定义一个变量,后使用./->对成员进行赋值
struct mydata gec;

strcpy(gec.name,“ggy”);

gec.age = 10;

2)使用初始化列表
struct mydata gec = {“helloworld”,20}; -> 等价于将"helloworld"拷贝到name成员的空间中,20就赋值给age;

struct mydata gec = {“helloworld”}; -> 后面没有赋值的成员 变量为0 指针为NULL

3)使用另外一个结构体给某个结构体整体赋值
struct mydata gec;

struct mydata A = {“hello”,20};

gec = A;

练习: 做一个通讯录,里面存放三个同学信息,每一个同学包含: 姓名,年龄,电话号码 -> 结构体数组。

  1. 先分别对三个同学注册。

  2. 输出三个同学的全部信息。

#include <stdio.h>

struct mydata{
       char name[20];
       int age;
       char tel[20];
};

int main()
{
       struct mydata A[3];
       int i; //0~2
       for(i=0;i<3;i++)
       {
              printf("pls input %d name",i+1);
              scanf("%s",A[i].name);
              printf("pls input %d age",i+1);
              scanf("%d",&A[i].age);
              printf("pls input %d tel",i+1);
              scanf("%s",A[i].tel);
       }
	printf("====================================================\n");
       for(i=0;i<3;i++)
       {
              printf("%s %d %s\n",A[i].name,A[i].age,A[i].tel);
       }
       return 0;
}

7、计算结构体类型在内存中占用的字节数。

基本数据类型:char 、short、int 、long、 float、 double -> 占用字节数固定。

例子: int a -> 占用4个字节

自定义结构体数据类型占用多少个?

struct mydata{
       char name[20];
       int age;
       char tel[20];
};

例子1:

struct mydata{
       char name[20];
       int age;
};

sizeof(struct mydata) = ? //24

例子2:

struct mydata{
       int age;
       char name[20];
};

sizeof(struct mydata) = ? //24

例子3:

struct mydata{
       int age;
       char name;
};

sizeof(struct mydata) = ? //8

例子4:

struct mydata{
       int age;
       char name;
       short a;
};

sizeof(struct mydata) = ? //8

例子5:

struct mydata{
       char a;
       char b;
       short c;
};

sizeof(struct mydata) = ? //4

=例子6:=

struct mydata{
       char a;
       short b;
       char c;   
};

sizeof(struct mydata) = ? //6

例子7:

struct mydata{
       char a;
       short b;
       char c;   
       int d;
       int e;
};

sizeof(struct mydata) = ? //16

例子

struct mydata{
       char a;
       int (*px)[3];
       char A[10];
       int b;
       char **p;
       short c;
       char a;
       short f;
       int e;
       char B[18];
};  //60

例子

struct mydata{
       short a;
       int (*px)(int,int);
       char b;
       char c;
      int d;
       char A[13];
       short e;
      int **p;
      int f;
       char *B[3];
}; //52

在这里插入图片描述
计算结构体占用空间大小的方法:
1)从上往下计算,而不是根据类型的大小来计算。
2)看看当前结构体中最大的成员占用是2/4 -> 2的话结果就是2的倍数 4的话结果就是4的倍数。
3)如果某个结构体成员计算完,但是当前没有对齐,那么剩余的字节大小就应该与下一个成员大小进行比较(字节对齐

  1. 下一个成员的大小大于当前行剩余的字节大小
    结果: 剩余的字节补0,下一个成员开辟新的一行空间。
  2. 下一个成员的大小小于/等于当前行剩余的字节大小
    结果:将下一个成员塞进剩余的字节中
    4)全部结构体成员处理完后,看看2的倍数/4的倍数来进行补0。

六、联合体

1、为什么会有联合体?

为了解决结构体在内存中占用比较大情况。

例子:

struct mydata{
       char name[20];
       int age;
       char tel[20];
};

sizeof(struct mydata) = 44; -> 占用非常多空间 -> 如果定义为联合体,则大大节省。

2、联合体定义方式与结构体一致,只需要修改关键词即可。

struct -> union

例子:

union mydata{
       char name[20];
       int age;
       char tel[20];
};

sizeof(union mydata) = 20

但是使用的时候,只能同时使用一个成员。
计算联合体的空间大小:
1)看看联合体哪个成员是占用空间最大的。

2)看看当前结构体中最大的成员占用是2/4 -> 2的话结果就是2的倍数 4的话结果就是4的倍数。

3)如果该成员占用字节数是2/4的倍数,那么联合体的大小就是该变量占用的字节数。

4)如果该成员占用字节数不是2/4的倍数,那么看情况来补0。

3、由于联合体只能同时使用一个成员,不能整体赋值。

例子:

union data{
       char name[10];
     int age;
};

union data A = {“ggy”,10};

编译警告; warning: excess elements in union initializer

==========================================================

#include <stdio.h>
#include <string.h>
#include <strings.h>

union data{
       char name[10];
       int age;
};

int main()
{
       union data A;
       strcpy(A.name,"ggy");
       printf("A.name = %s\n",A.name);
       bzero(A.name,sizeof(A.name));
       A.age = 10;
       printf("A.age = %d\n",A.age)return 0;
}

=====================================================

4、在联合体中,所有成员的起始地址都是一致的。

#include <stdio.h>
#include <string.h>
#include <strings.h>
union data{
       char name[10];
      int age;
};

int main()
{
       union data A;
       printf("A.name:%p\n",A.name);
       printf("A.age:%p\n",&A.age);
       return 0;
}

执行:
A.name:0xbfb3b220

A.age:0xbfb3b220

5、联合体也是可以作为函数的参数

#include <stdio.h>
#include <string.h>
#include <strings.h>
union data{

       char name[10];
       int age;
};

void fun(union data B)  //B = A
{
      printf("B.name = %s\n",B.name);
       return;
}

int main()
{
       union data A;
       strcpy(A.name,"ggy");
       fun(A);
       return;
}

七、枚举类型

1、什么是枚举类型?

枚举类型其实就是int类型常量,意义就是给常量一个身份。
例子: ok -> 1 warning -> 0 error -> -1

2、枚举类型一般作用地方?

1)可以作用于switch语句。
2)在函数返回值。 return 枚举类型数据类型

void fun()
{
       if(xxxx)
              return warning;  -> 别人看起来就知道返回值是什么情况。
       if(yyyy)
              return ok;
       if(zzzz)
              return error;
}

3、如何定义枚举类型?

关键词: enum

模型:

enum mydata{
       ok,    //如果没有赋值,第一个成员默认从0开始!   //0
      warning,  //后面没有赋值的成员默认在前面的基础+1  //1
       error,                                         //2
};
enum mydata{
       ok = 1,  
       warning = 0,
       error = -1,
}; 
enum mydata{
       ok = 10,  
       warning = 20,
      error, //21
};

八、typedef关键词

1、什么是typedef?作用是什么?

typedef其实就是 type+ define,给一种数据类型(基本数据类型/非基本数据类型)取一个新的别名

例子: 给int这种类型取一个新的名字叫aaa。 -> 很少给基本数据类型取别名 1%

   给struct mydata这种数据类型取新的名字叫mydata。  -> 给非基本数据类型取别名  99%

作用: 简化复杂的数据类型。

2、如何使用typedef给数据类型取别名?

使用格式: typedef + 数据类型 + 新的名字

1)给int这种类型取一个新的名字叫aaa。
typedef int aaa; -> 原则来typedef后面的这个"int aaa"看看,类型是int,剩下的就是aaa就是它的新名字。

在后面的代码中,定义一个整型变量:aaa a;

例子:

#include <stdio.h>
int main()
{
       typedef int aaa; //int 等价于 aaa
       aaa a = 10;
       printf("a = %d\n",a);
       return 0;
}

2)给struct mydata这种数据类型取新的名字叫mydata。

typedef struct mydata{
       char name[20];
       int age;  
}mydata;  //struct mydata 等价于 mydata
 
mydata bbb;  -> 定义一个结构体变量。
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章