再识C中的结构体,识C结构体

作者:ca88编程

  在头里认知C中的结构体中自己介绍了结构体的基础知识,上边通过这段代码来回看一下:

再识C中的结构体,识C结构体

  在这段日子认知C中的结构体中自己介绍了结构体的基础知识,上边通过这段代码来回想一下:

 1 #include<stdio.h>
 2 #define LEN 20
 3 
 4 struct Student{            //定义结构体
 5     char name[LEN];
 6     char address[LEN];
 7     int age;
 8 };
 9 
10 int main(int argc, char* argv[])
11 {
12     struct Student s = {        //初始化
13         "bluemsun","NENU",25
14     };    
15     
16     struct Student * p;            //定义一个指向结构体的指针
17     p = &s;                        //为指针赋值
18 
19     printf("s.name = %s,s.address = %s,s.age = %dn",s.name,s.address,s.age);
20     printf("p->name = %s,p->address = %s,p->age = %dn",p->name,p->address,p->age);    
21 }

  那是二个相比较轻便的例证程序,在构造体Student中大家定义四个char数组,今后来虚拟那样三个题目。我们在日常亟需选拔数组的时候都以能够用指针替代数组作为参数使用,那么在上头的次第中是或不是用上边这段代码替代结构体的定义呢?

1 struct new_Student{            //定义结构体
2     char * name;
3     char * address;
4     int age;
5 };

  答案是确定的,不过只怕会凌驾有的劳动。思量上边一段代码:

1 struct new_Student s1 = {"zhouxy","EFGH",25};    
2 struct Student s2 = {"bluemsun","ABCD",26};    

  这段代码是正确的。不过思量当中的字符串存款和储蓄在何地?对于struct Student变量s2来讲,字符串存款和储蓄在构造内部,那个布局总共分配了36个字节来储存多少个字符串。但是对于new_Student变量s1的话,字符串是存放在在编写翻译器存款和储蓄字符串常量的别的地方。new_Student结构中只是是存放了多个地方而已。所以一旦急需二个布局来贮存在字符串,请使用字符数组成员。那么是还是不是利用指针的章程就实在不能不负职务内部存款和储蓄器的分配呢?答案是或不是认的。在此地自身教学了关于C中的函数malloc(),那么些函数是足以在运维期动态分配内部存储器的。所以纵然能整合在此处来利用,那就达成了大家的怀念了。思虑上面这段代码:

 1 #include<stdio.h>
 2 #include<string.h>
 3 #include<stdlib.h>
 4 
 5 struct new_Student{            //定义结构体
 6     char * name;
 7     char * address;
 8     int age;
 9 };
10 
11 int main(int argc, char* argv[])
12 {
13     char str[] = "zhouxy";
14     struct new_Student s1;    
15     //分配用来存放名字的内存
16     s1.name = (char*)malloc(strlen(str) 1);
17     //把名字复制到已分配的内存中
18     strcpy(s1.name,str);
19     
20     printf("s1.name = %sn",s1.name);
21 }

  上边代码是科学的,大家用malloc()函数分配存储空间,然后把字符串复制到新分配的空中。这里要知道的是:name字符串不是被储存在结构中,而是被保存在由malloc()函数管理的内部存款和储蓄器中。结构中只是是保存了name字符串的地址而已。还应该有有些要记得的是:大家选用malloc()函数分配内部存款和储蓄器之后要调用free()函数,不然大概会唤起"内部存款和储蓄器泄露"。

在前边认识C中的结构体中本身介绍了结构体的基础知识,上边通过这段代码来回看一下: 1 #includestdio.h 2 #defi...

内部存款和储蓄器管理

7.5 数组做参数未有别本机制,数组做参数退化为指针

#include <stdio.h>
#include <stdlib.h>
void test(int a[10]){
    printf("n %d",sizeof(a));//4
}

void changearr(int a[10]){
    int i = 0;
    for(;i<10;i  ){
        a[i] =11;
    }
}


int main()
{
    int i = 0;
    int a[10] = {1,2,3,4,5,6,7,8,9,0};
    test(a);
    changearr(a);
    for(;i<10;i  ){
        printf("n =",a[i]);// 12 13 14 15 16 17 18 19 20 11
        //这里数组的元素都发生了改变,说明在changearr函数中并没有产生副本,如果产生副本值就不会发生改变了
    }
    return 0;
}
1 struct new_Student{            //定义结构体2     char * name;3     char * address;4     int age;5 };

}

8 内存

1 struct new_Student s1 = {"zhouxy","EFGH",25};    2 struct Student s2 = {"bluemsun","ABCD",26};    

位域长度无法超越该类型的位数
能够将该类型所占的其余位忽略
能够无位域名,表示填充可能调节地点

5. void类型的功力:对函数重返的限定 和对函数参数的界定

其余类型的指针都能够一向赋值给void*,不必要进行强制类型调换

  那是一个比较简单的例子程序,在布局体Student中大家定义多个char数组,今后来设想那样三个主题素材。大家在平常亟需利用数组的时候都以足以用指针取代数组作为参数使用,那么在地点的程序中是还是不是用上边这段代码代替结构体的概念呢?

malloc 
calloc
free
realloc

8.5 静态局地变量在编写翻译的时候就最早化了,

int main()
{
    int i =0;
    for(;i<10;i  ){
        int a = 10;//用完就回收
        static int b= 10;//与程序共存亡,静态局部变量,编译的时候就初始化了
        a =1;
        b =1;
         printf("a=%d,b=%dn",a,b);//a永远都是11,b是11 12 13 14.。。。
    } 
    return 0;
}

  这段代码是科学的。然则思虑在那之中的字符串存款和储蓄在哪儿?对于struct Student变量s2来讲,字符串存储在构造内部,那一个布局总共分配了叁十多个字节来存储三个字符串。可是对于new_Student变量s1的话,字符串是贮存在编写翻译器存款和储蓄字符串常量的另外地点。new_Student结构中只是是寄放在了多个地点而已。所以一旦必要三个协会来存放在字符串,请使用字符数组成员。那么是否利用指针的办法就实在不能够成功内部存款和储蓄器的分配呢?答案是不是定的。在那边本身教学了关于C中的函数malloc(),这么些函数是足以在运转期动态分配内部存款和储蓄器的。所以若是能组成在此间来使用,这就达成了大家的思索了。思量上边这段代码:

}

2、 calloc(数量,大小)  分配到堆区

viid * calloc(unsigned n,unsigned size);   上例改为 int a = sizeof(stu);   stu *b = (stu*) calloc(2,a);
其功用是在内部存款和储蓄器的动态存款和储蓄区中分红n个长度位 size的总是空间,那一个空间一般非常大,足以保存四个数组
可以为一维数组开辟动态积攒空间,n为数组成分个数,各种成分长度位size,那正是动态数组。

calloc  ----->malloc
如:calloc(m,n)   ---->malloc(m*n);

malloc  ----->calloc
如:malloc(a)  ---->calloc(1,a);

例如:int *a = (int*)calloc(10,sizeof(int));  if(a==NULL)  三十八个字节,等于分配长度为 10 的一维数组

 

3、free(指针变量) 
void free(void *p);   p=NULL;
其职能是刑满释放指针变量p所指向的动态空间,那部分空间能重复被别的变量使用,p应是新近一次调用calloc或malloc函数时获得的函数重临值
free(p)释放指针变量p所指向的已分配的动态空间,可是注意,当p被释放后,p变为野指针,所以必定要加一句  p=NULL或p=0;
free函数无重回值
动态分配的长空用完后,必得求自由。

***** 注意 *****
在采取malloc恐怕cmalloc申请空间时,如 int *p  = (int*)malloc(40); 
p指针差别意开展 p  或许 --p,那样将产生部分内部存款和储蓄器区域无法归还,导致内存走漏。

4、realloc(指针,大小)
void *realloc(void *p,unsigned int size);
假如已经经过malloc函数获取到了动态的半空中,想更改大小,能够行使relloc函数重新分配
用realloc 函数讲p所针对动态空间的尺寸改为size,p的值不改变,假诺重新分配不成功,再次回到NULL(  "p=(int*)relloc(p,20)"  那句话有失常态,一旦重新分配不成事,p=NULL,将会导致p指向的本来的空间也力无法支找到,不可能偿还,将招致内部存储器泄露);

自定义类型 轻易的用贰个新的花色名取代原有的花色名 typedef int Integer; int i,j; Integer k; //注脚结构体 typedef struct{ int month; int day...

1.8.2

void printArray(char strArr[4][10],int num){
    int i = 0;
    for(;i<num;i  ){
        printf("%8s",strArr[i]);
    }
} 

void sortArray(char strArr[4][10],int num){
    int i = 0, j = 0;
    char temp[10];
    for(i=0;i<num;i  ){
        for(j=i 1;j<num;j  ){
            if(strcmp(strArr[i],strArr[j])<0){
                strcpy(temp,strArr[i]);
                strcpy( strArr[i],strArr[j]);
                strcpy(strArr[j],temp); 
            }
        }
    }
}


int main(int argc, char *argv[]) {
    int i=0,j=0;
    char *temp = NULL;
    char ArrayStr[4][10] ={"bbbb","aaaa","dddd","cccc"};
    printArray(ArrayStr,4);
    sortArray(ArrayStr,4);
    printf("n");
    printArray(ArrayStr,4);
    return 0;
}
 1 #include<stdio.h> 2 #define LEN 20 3  4 struct Student{            //定义结构体 5     char name[LEN]; 6     char address[LEN]; 7     int age; 8 }; 9 10 int main(int argc, char* argv[])11 {12     struct Student s = {        //初始化13         "bluemsun","NENU",2514     };    15     16     struct Student * p;            //定义一个指向结构体的指针17     p = &s;                        //为指针赋值18 19     printf("s.name = %s,s.address = %s,s.age = %dn",s.name,s.address,s.age);20     printf("p->name = %s,p->address = %s,p->age = %dn",p->name,p->address,p->age);    21 }

4.2.1数据类型能够理解为创制变量的模型:是稳固内部存款和储蓄器大小的小名。

  答案是早晚的,不过大概会碰到有的劳动。思索下边一段代码:

}

6.11 指向结构体的指针的二种艺术

#include <stdio.h>
#include <stdlib.h>

struct Info{
int num;
float score;
};

int main()
{
    struct Info info;
    struct Info *pinfo = &info;
    struct Info *p = (struct Info *)malloc(sizeof(struct Info));
    printf("Hello world!n");
    return 0;
}

  上面代码是不移至理的,我们用malloc()函数分配存款和储蓄空间,然后把字符串复制到新分配的上空。这里要理解的是:name字符串不是被存放在布局中,而是被保留在由malloc()函数管理的内部存款和储蓄器中。结构中只有是保留了name字符串的地点而已。还大概有一点要记得的是:大家运用malloc()函数分配内部存款和储蓄器之后要调用free()函数,不然大概会挑起"内存走漏"。

//定义指针
typedef char *String
String p,s[10];

6.6 结构体内二级指针

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Teacher{
    char name[62];
    int age;
    char *title;
    char **stuname;
};

struct Teacher * createArr(int num){
    int i=0;
    int j=0;
    struct Teacher* p =(struct Teacher*)malloc(num*sizeof(struct Teacher));
    if(p==NULL){
        printf("n分配内存失败");
    }
    for(i=0;i<num;i  ){
        memset(&p[i],0,sizeof(struct Teacher));
        memset(p i,0,sizeof(struct Teacher));
        p[i].title = (char*)malloc(128*sizeof(char));
        memset(p[i].title,0,128*sizeof(char));
        {
            char **p2 = (char**)malloc(3*sizeof(char *));
            for(j=0;j<3;j  ){
                p2[j] = (char *)malloc(128*sizeof(char));
                memset(p2[j],0,128*sizeof(char)); 
            }
            p[i].stuname = p2;
        }
    }
    return p;
} 



int main(int argc, char *argv[]) { 
    struct Teacher *t = createArr(4);

    return 0;
}
 1 #include<stdio.h> 2 #include<string.h> 3 #include<stdlib.h> 4  5 struct new_Student{            //定义结构体 6     char * name; 7     char * address; 8     int age; 9 };10 11 int main(int argc, char* argv[])12 {13     char str[] = "zhouxy";14     struct new_Student s1;    15     //分配用来存放名字的内存16     s1.name = (char*)malloc(strlen 1);17     //把名字复制到已分配的内存中18     strcpy(s1.name,str);19     20     printf("s1.name = %sn",s1.name);21 }

|   a 1   |   空 2   |   b 3  |   c  2 |      //共占8个位

2.3

在编写翻译时代,char *p = malloc(100);只会为p分配多个字节,唯有当这段代码试行的时候,才会分配malloc的九十多个字节,如若条件不树立以来,不会分配那九十多个字节。可是char p[100];这句代码,在编写翻译器就分配了九19个字节。

  …..
……
}stu;

1.9.4数组指针的三种概念格局

int main(int argc, char *argv[]) {
    int i =0; 
    int b[5] = {1,2,3,4,5};  
    int(*p)[5] = NULL;
    p = &b;
    for(;i<5;i  ){
        printf("M",(*p)[i]);
    }
    return 0;
}

int main(int argc, char *argv[]) {
    int i =0; 
    int b[5] = {1,2,3,4,5};  
    typedef int(*parr)[5];
    parr p = NULL;
    p = &b; 
    for(;i<5;i  ){
        printf("M",(*p)[i]);
    }
    return 0;
}

}

7.函数

}

1.9.5二维数组的数组名

二维数组的数组名是首成分的地方
二维数组的数组名是贰个指南针常量
二维数组的数组名是一个数组指针

//myarray是数组首元素的地址 
//myarray是一个指针常量 
//myarray是一个数组指针 
int main(int argc, char *argv[]) {
    int i=0;
    int j=0;

    int a[5]={4,2,7,3,9};
    int myarray[3][5];
    int (*p)[5] = &a;

    for(;i<5;i  ){
        printf("=",(*p)[i]);
    }

    for(;i<3;i  ){
        for(;j<5;j  ){
            myarray[i][j]=i j;
        }
    }

    printf("nd,d",myarray,myarray 1);//步长是20 
    printf("nd,d",&a,&a 1);//步长是20 
    p  = myarray;//p和myarray都是数组指针  
    return 0;
}

   int month;
   int day;
   int year;
}Date;                   //用typedef 生命的结构体,原结构体变量形成了贰个新的类型了

4.1数组做函数参数会落后为指针

在花样参数里面出现的char buf[30]或者int a[10]等,编写翻译器会将他算得指针,不会为其积极性的多分配内部存款和储蓄器。

void func(int a[10]){
     printf("n%d",sizeof(a));//4
} 

int main(int argc, char *argv[]) {
    int a[10];
    printf("n%d",sizeof(a));//40
    func(a);
    return 0;
}

如:
//分配37个字节的连日空间,让a指向新空间的首地址
int *a =(int*)malloc(40);
ca88编程,//判读是不是申请成功
if(a == NULL){

6.8深拷贝

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct Teacher{
    char name[62];
    int age;
    char *title;

}; 

//深拷贝
//为对象又开辟了一块新的内存空间 
void copyObj(struct Teacher* from,struct Teacher* to){
    memcpy(to,from,sizeof(struct Teacher));
    to->title = (char*)malloc(128);
    to->age = from->age;
    strcpy(to->title,from->title);
}


int main(int argc, char *argv[]) { 
    struct Teacher ta;
    struct Teacher tb;
    ta.age = 11;
    ta.title = (char*)malloc(128*sizeof(char));
    strcpy(ta.title,"xihe");
    copyObj(&ta,&tb);
    //ta和tb的title指向两个不同的内存空间,释放两次不会报错 
    if(ta.title!=NULL){
        free(ta.title);
        printf("n free ta.title");
    }

    if(tb.title!=NULL){
        free(tb.title);
         printf("n free ta.title");
    } 
    return 0;
}

另一种:使用typedef
typedef struct Student{

7.6 return 的别本机制,return的别本在寄存器

#include <stdio.h>
#include <stdlib.h>
int go(){
    int a = 3;//auto变量,go函数执行完之后就销毁了
    return a;//副本,在寄存器
}


int main()
{
    printf("n %d",go()); // 3
 //   printf("d %x",&go()); 函数的返回值不能取地址
    return 0;
}

typedef int Integer;
int i,j;
Integer k;

6.10 链表的实现

#ifndef LIST_H
#define LIST_H

typedef struct ListElmt_{
    void *data;
    struct ListElmt_ *next;
}ListElmt;


typedef struct List_{
    int size;
    int (*match)(const void *key1,const void *key2);
    void (*destroy)(void *data);
    ListElmt *head;
    ListElmt *tail;
}List;

void list_init(List *list,void(*destroy)(void *data));
void list_destroy(List *list);
int list_ins_next(List *list,ListElmt *element,const void *data);
int list_rem_next(List *list,ListElmt *element,void **data);

#define list_size(list) ((list)->size)
#define list_had(list) ((list)->head)
#define list_tail(list) ((list)->tail)
#define list_is_tail(list,element) ((element)==(list)->tail?1:0)
#define list_is_head(list,element) ((element)==(list)->head?1:0)
#define lsit_data(element) ((element)->data)
#define list_next(element) ((element)->next)

#endif

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "list.h"

void list_init(List *list,void (*destroy)(void *data)){
    list->size = 0;
    list->destroy = destroy;
    list->head = NULL;
    list->tail = NULL;
}

void list_destroy(List *list){
    void *data;
    while(list_size(list)>0){
        if(list_rem_next(list,NULL,(void  **)&data)==0 && list->destroy!=NULL){
            //调用一个用户自定义的函数释放资源
            list->destroy(data); 
        }
    }
    memset(list,0,sizeof(list));
    return;
}

//在指定元素element后插入新的元素 
int list_ins_next(List *list,ListElmt *element,const void *data){
    //创建新的元素
    ListElmt *newElement;
    if((newElement=(ListElmt*)malloc(sizeof(ListElmt)))==NULL){
        return -1;
    } 
    newElement->data = data;

    if(element==NULL){
        if(list_size(list)==0){
            list->tail = newElement;
        }
        newElement->next = list->head;
        list->head = newElement;
    } else{
        if(element->next == NULL){
            list->tail = newElement;
        }
        newElement->next = element->next;
        element->next = newElement; 
    }
    list->size  ;
    return 0;
}

int list_rem_next(List *list,ListElmt *element,void **data){
    ListElmt *oldElement;
    if(list_size(list)==0){
        return -1;
    }
    if(element==NULL){
        *data = list->head->data;
        oldElement = list->head;
        list->head = list->head->next;
        if(list_size(list)==1){
            list->tail = NULL;
        }
    }else{
        if(element->next == NULL){
            return -1;
        }
        *data = element->next->data;
        oldElement = element->next;
        element->next = element->next->next;
        if(element->next == NULL){
            list->tail = element;
        }
    }
    free(oldElement);
    list->size--;
    return 0;
}

C语言 还允许创建动态分配区域,以寄存一些一时用的数码,那么些数量须要时刻开采,不须求的时候试试释放,那一个多少是一时寄放在叁个极度
的跋扈存款和储蓄区,成为 堆 区

5.1即使函数未有重临值,那么应评释为 void 类型

上边这段程序编写翻译和平运动转都未曾难点

add(int a,int b){
    return a b;
}

int main(int argc, char *argv[]) {

       printf("%d",add(2,3));
       return 0; 
}

敲定:在C语言中,凡不加重临值类型限定的函数,就能被编写翻译器作为重临整型值管理。但是洋洋程序员却误认为其为void类型
为此,为了避免混乱,在编辑C/C 程序时,对于其余函数都不能够不贰个不漏地钦命其种类。若是函数未有再次回到值,一定要表明为void类型。那既是前后相继能够可读性的内需,也是编制程序标准性的供给。其余,加上void类型注解后,也得以发布代码的“自注释”成效。代码的“自注释”即代码能和煦注释自身

1、malloc(大小)   分配到堆区
void * malloc(usingned int size);单位是(byte)字节数 
          其效果是在剧情的动态存款和储蓄区分配三个长短位 side 空间,此函数是三个指针型函数,重回的指针是该分红区域的伊始的地点(或首地方)
 注意指针的体系位void 即不指向其它类型的数据,只提供二个地址。放什么品种的数量,强制转换个地方什么样品种。
 假设函数未能得逞申请到空中(内部存款和储蓄器不足),重返空指针 NULL

1.8.1

void printArray(char **strArr,int num){
    int i = 0;
    for(;i<num;i  ){
        printf("%8s",strArr[i]);
    }
} 

void sortArray(char **strArr,int num){
    int i = 0, j = 0;
    char *temp = NULL;
    for(i=0;i<num;i  ){
        for(j=i 1;j<num;j  ){
            if(strcmp(strArr[i],strArr[j])<0){
                temp = strArr[i];
                strArr[i] = strArr[j];
                strArr[j] = temp;
            }
        }
    }
}


int main(int argc, char *argv[]) {
    int i=0,j=0;
    char *temp = NULL;
    char *ArrayStr[] ={"bbbb","aaaa","dddd","cccc"};
    printArray(ArrayStr,4);
    sortArray(ArrayStr,4);
    printf("n");
    printArray(ArrayStr,4);
    return 0;
}

例如:
struct sta{

1.6.5对此参数是指针类型的精通:站在c编译器的角度看,对于形参,假若是指针类型,c编写翻译器只会分配七个字节内部存款和储蓄器

stu *p = (stu*) malloc( sizeof(stu)*2 );
//判断
if(p==NULL){

8.1声称能够有多少个实体只好有三个

扬言只是用来申明存在,定义是在内部存款和储蓄器中开荒实体(壹位方可有五个居民身份证号,但是真的的人不得不有一个)

//这段代码没有问题
#include <stdio.h>
#include <stdlib.h>
int a;
int a; 
int main()
{ 
    return 0;
}

//这段代码有问题
#include <stdio.h>
#include <stdlib.h> 
int main()
{
    int a;
    int a;
    return 0;
}

###
typedef 只是对已经存在的品种制订三个新的花色名,而未有创制新的档案的次序
用typedef证明数组类型 指针类型 结构体类型 共用体类型 枚举类型等
typedef 与 #define 表面上有相似之处
当分歧的源文件中用到同样类别的多寡时,常用typedef声美素佳儿(Friso)些数据类型,能够吧全数的typedef名称证明单独放在二个头文件中
动用typedef名称有助于程序的通用与移植,有的时候程序会凭仗与硬件的表征,用typedef类型便于移植。

5.3小心使用void类型指针

ANSI标准规定:不可能对void类型指针实行算法操作那是因为:举办算法操作的指针必需是鲜明知道其针对性数据类型大小的

本文由ca88发布,转载请注明来源

关键词: ca88网址 结构 再识 c/c++ ca