【C语言】学习笔记8——结构struct(1)

1. 先看个例子

#include <stdio.h>
#include <string.h>
#define MAXTITL 41
#define MAXAUTL 31

struct book {                   /*结构模板,标记是 book */
    char title[MAXTITL];
    char author[MAXAUTL];
    float value;
}; 

char * s_gets(char *, int);

int main()
{
    struct book library;    /* 把 library 声明为一个 book 类型的变量*/
    printf("请输入书的标题:\n");
    s_gets(library.title, MAXTITL);
    printf("现在输入书的作者姓名:\n");
    s_gets(library.author, MAXAUTL);
    printf("现在输入书本的价格:\n");
    scanf("%f", &library.value);
    
    printf("%s by %s: $%.2f\n", library.title, library.author, library.value);
    printf("%s: \"%s\"($%.2f)\n", library.author, library.title, library.value);
    printf("Done.\n");
    return 0;
 } 
 
 char * s_gets(char *st, int n)
 {
     char * ret_val;
     char * find;
     ret_val = fgets(st, n, stdin);
     if (ret_val)
     {
         find = strchr(st, '\n'); //查找换行符
        if (find)            //如果地址不是NULL 
            *find = '\0';   //在此放置一个空字符
        else
            while (getchar() != '\n')
                continue;    //处理输入行中剩余的字符 
     }
     return ret_val;
 }
 
 /*
 output:
 请输入书的标题:
我与地坛
现在输入书的作者姓名:
史铁生
现在输入书本的价格:
20
我与地坛 by 史铁生: $20.00
史铁生: "我与地坛"($20.00)
Done.
 */

结构变量:为了提高C语言表示数据的能力。

  比如说描述一本书,我们会用一个char数组表示书名, 再用一个char数组表示作者,一个float表示书的描述,但是我们要描述很5本书的时候,我们就得用5个char数组分别表示5本书名,5个char数组表示五本书的作者,5个float表示五本书的价格。这样做很麻烦,而且不容易维护他们之间的关系。所以就有了结构。

  结够有点像面向对象,但是只有属性,没有行为;

2. 建立结构声明,并声明一个结构变量

struct book {               /*带标记定义结构,可重用*/   
    char title[MAXTITL];
    char author[MAXAUTL];
    float value;
}; 

struct book library;

struct {               /*不带标记定义结构, 不可重用*/   
    char title[MAXTITL];
    char author[MAXAUTL];
    float value;
} library;             /*定义结构的同时,声明一个结构变量。*/

 

3. 结构的内存模型

【C语言】学习笔记8——结构struct(1)

 

4. 初始化结构变量

struct book library = {   /*按顺序初始化*/
    "我与地坛""史铁生"20.00
};

struct book library = {   /*按成员名称初始化*/
    .value = 20.00,
    .author = "史铁生",
    .title = "我与地坛"
};

struct book library = {   /*局部初始化*/
    .value = 20.00
};

struct book library = {   /*瞎j8初始化*/
    .value = 20.00,
    .author = "史铁生"5.00    
};                 //因为value紧跟author, 最终value = 5.00,

 

5. 访问结构的数据,用结构成员运算符 (. )

6. 声明结构数组

struct book library[5];

7.结构数组内存模型

【C语言】学习笔记8——结构struct(1)

8. 嵌套结构

#include <stdio.h>
#define LEN 20

const char * msgs[5] = 
{
    "  Thank you for the wonderful evening, ",
    "You certainly prove that a ",
    "is a special kind of guy. We must get together",
    "over a delicious ",
    " and have a few laughs"
};

struct names{
    char first[LEN];
    char last[LEN];
}; 

struct guy{
    struct names handle;
    char favfood[LEN];
    char job[LEN];
    float income;
};

int main()
{
    struct guy fellow = {
        {"Ewen", "Villard"},
        "grilled salmon",
        "personality coach",
        68112.0
    };
    printf("Dear %s, \n\n", fellow.handle.first);
    printf("%s%s.\n", msgs[0], fellow.handle.first);
    printf("%s%s\n", msgs[1], fellow.job);
    printf("%s\n", msgs[2]);
    printf("%s%s%s", msgs[3], fellow.favfood, msgs[4]);
    if (fellow.income > 150000.0)
        puts("!!");
    else if (fellow.income > 75000.0)
        puts("!");
    else
        puts(".");
    
    printf("\n%40s%s\n", " ", "See you soon,");
    printf("%40s%s\n", " ", "Shalala");
    
    return 0;
}

/*
output:
Dear Ewen,

  Thank you for the wonderful evening, Ewen.
You certainly prove that a personality coach
is a special kind of guy. We must get together
over a delicious grilled salmon and have a few laughs.

                                        See you soon,
                                        Shalala
*/

9. 指向结构的指针

#include <stdio.h>
#define LEN 20

struct names{
    char first[LEN];
    char last[LEN];
}; 

struct guy{
    struct names handle;
    char favfood[LEN];
    char job[LEN];
    float income;
};

int main()
{
    struct guy fellows[2] = {
        {
            {"Ewen", "Villard"},
            "grilled salmon",
            "personality coach",
            68112.0
        },
        {
            {"Rondeny", "Swillbelly"},
            "tripe",
            "tabloid editor",
            432400.00
        }
    };
    
    struct guy * him; /* 声明一个指向结构的指针 */
    printf("address #1: %p #2: %p\n", &fellows[0], &fellows[1]);
    him = &fellows[0];  /* 告诉编译器该指针指向何处 */
    printf("him->income is $%.2f: (*him).income is $%.2f\n", him->income, (*him).income);
    him++;
    printf("him->favfood is %s: (*him).handle.last is %s\n", him->favfood, (*him).handle.last);
    return 0;
}

/*
output:

address #1: 000000000062FD90 #2: 000000000062FDE4
him->income is $68112.00: (*him).income is $68112.00
him->favfood is tripe: (*him).handle.last is Swillbelly

*/

10. 用指针访问结构成员

struct guy * him;   //声明一个指向结构变量的指针

struct guy barney;

him = &barney;      //让指针指向结构变量barney
him->income;      //使用 -> 运算符访问结构的成员变量,即 barney.income

(*him).income       //将指针解引用,即barney.income

11. 向函数传递结构的信息

  a. 传递结构成员

  b. 传递结构地址

  c. 传递结构

#include <stdio.h>
#define FUNDLEN 50

struct funds {
    char bank[FUNDLEN];
    double bankfund;
    char save[FUNDLEN];
    double savefund;
};

double sum1(double, double);
double sum2(const struct funds *); 
double sum3(struct funds);

int main()
{
    struct funds stan = {
        "Garlic-Melon Bank",
        4032.27,
        "Lucky's Savings and Loan",
        8543.94
    };
    printf("Stan has a total if $%.3f.\n", sum1(stan.bankfund, stan.savefund));  //向函数传递结构成员 
    printf("Stan has a total if $%.3f.\n", sum2(&stan));  //向函数传递结构的地址 
    printf("Stan has a total if $%.3f.\n", sum3(stan));  //向函数传递结构
    
    return 0;
}

double sum1(double x, double y)
{
    return (x + y);
}

double sum2(const struct funds * money)
{
    return (money->bankfund + money->savefund);
}
double sum3(struct funds moolah)
{
    return (moolah.bankfund + moolah.savefund);
}

/*
output:

Stan has a total if $12576.210.
Stan has a total if $12576.210.
Stan has a total if $12576.210.

--------------------------------
*/

12.  结构中的字符数组和指针

  假设有一个结构声明

#define LEN 20
struct names{
    char first[LEN];
    char last[LEN];
};

  使用指向char的指针来代替字符数组

struct pnames{
    char * first;
    char * last;
};

  这样做是可以的。但是会带来麻烦。考虑以下代码

struct names veep = {"Talia", "Summers};
struct pnames treas = {"Brad", "Fallingjaw"};
printf("%s and %s\n", veep.first, treas.first);

  代码运行都没有问题,内存分配是怎么做的?

  对于struct names 类型的结构变量 veep , 以上字符串都存储在结构内部,结构总共要分配40字节存储姓名。

  然而对于 struct pnames类型结构变量 treas,以上字符串存储在编译器存储常量的地方,结构本身只存储了两个地址, 在我们的系统*占16字节(64位系统一个地址是8字节)。 struct pnames 结构不用为字符串分配任何存储空间。他使用的是存储在别处的字符串。

  考虑以下代码

struct names accountant;
struct pnames attorney;
puts("Enter the last name of your accountant:");
scanf("%s", accountant.last);
puts("Enter the last name of your attorney:");
scanf("%s", attorney.last);  //潜在的危险

  用户的输入存储到哪里去了? 

       对于 accountant, 它的姓被存储在names的last中。

  对于 attorney,它的姓被存储在 attorney.last所指向的地址中。由于attorney未被初始化,所以attorney.last可能指向任何一个地址,所以这个操作可能会造成不想要的修改。

  因此,如果要用结构存储字符串,用字符数组作为成员笔记简单。用指向char的指针也行,但是误用会导致严重的问题。

 

上一篇:【Linux】CentOS操作和问题汇总


下一篇:【C语言】学习笔记10——其他数据结构