【C 语言】结构体相关 的 函数 指针 数组(二)

3. 结构体数组





(1) 结构体数组声明初始化





声明结构体数组 :


-- 声明结构体的时候声明结构体数组 : 格式为 : struct 结构标记 {} 数组名[];


-- 使用结构标记声明结构体数组 : 格式为 : struct 结构标记 数组名[];






结构体数组声明初始化 :


-- 逐个元素初始化 : 数组名[] = {{结构体1}, {结构体2}};


-- 总体初始化 : 数组名[] = {常量1, 常量2 ...};




结构体初始化 :



/*************************************************************************
    > File Name: array_struct.c
    > Author: octopus
    > Mail: octopus_work.163.com 
    > Created Time: 2014年03月24日 星期一 16时40分15秒
 ************************************************************************/
#include<stdio.h>
/*
 * 声明结构体 
 * 同时也声明结构体类型数组
 * 为数组初始化
 * 直接将每个结构体成员的值依次列出即可
 */
struct student
{
    char *name;
    int age;
} team1[] = 
{
    "Tom", 12,
    "Jack", 13
};
int main(int argc, char **argv)
{
    int i;
    /*将每个结构体初始化, 赋值, 每个结构体初始化内容使用 花括号括起来*/
    struct student team2[] = {{"CJ", 34}, {"KI", 32}};
    for(i = 0; i < 2; i++)
    {
  printf("team1 : team1[i].name = %s, team1[i].age = %d \n", team1[i].name, team1[i].age);
    }
    for(i = 0; i < 2; i++)
    {
  printf("team2 : team2[i].name = %s, team2[i].age = %d \n", team2[i].name, team2[i].age);
    }
    return 0;
}

执行结果 :


octopus@octopus-Vostro-270s:~/code/c/struct$ gcc array_struct.c 
octopus@octopus-Vostro-270s:~/code/c/struct$ ./a.out 
team1 : team1[i].name = Tom, team1[i].age = 12 
team1 : team1[i].name = Jack, team1[i].age = 13 
team2 : team2[i].name = CJ, team2[i].age = 34 
team2 : team2[i].name = KI, team2[i].age = 32



(2) 结构体数组示例程序



需求 : 实现一个统计 C 语言关键字出现次数;




代码 :



/*************************************************************************
    > File Name: word_count.c
    > Author: octopus
    > Mail: octopus_work.163.com 
    > Created Time: 2014年03月24日 星期一 17时12分32秒
 ************************************************************************/
#include<stdio.h>
#include<ctype.h>
#include<string.h>
#define MAXWORD 10
/*
 * 定义结构体类型 key 
 * 该类型结构体中存储一个 字符串(关键字) 和 int 数据(关键字出现次数)
 * 同时声明一个结构体数组
 * 对结构体数组进行初始化
 *
 *
 */
struct key
{
        char *word;
        int count;
}key_count[] = 
{
        "auto", 0,
        "break", 0,
        "case", 0,
        "char", 0,
        "const", 0,
        "continue", 0,
        "default", 0,
        "void", 0,
        "volatitle", 0,
        "while", 0
};
int main(int argc, char **argv)
{
        int n;
        char word[MAXWORD];
        /*循环接收字符串, 如果字符串与结构体数组中匹配, 结构体的count ++*/
        while(getword(word, MAXWORD) != EOF)
                if(isalpha(word[0]))
                        if((n = binsearch(word, key_count, 10)) >= 0)
                                key_count[n].count++;
        /*打印大于0的关键字 及 个数*/
        for (n = 0; n < 10; n ++)
                if(key_count[n].count > 0)
                        printf("%2d %s\n", key_count[n].count, key_count[n].word);
        return 0;
}
/* 
 * 重要api解析 :
 *  int getc(FILE *stream) 从标准输入流中读取字符
 *  int ungetc(int c, FILE *stream) 将字符退回到标准输入流中
 *  int isspace(int c) 判断字符是否是 空格 '\f' '\r' '\n' '\t' '\v'
 *  int isalpha(int c) 判断是否是字母
 */
int getword(char *word, int lim)
{
        int c, getc(FILE*), ungetc(int, FILE*);
        char *wp = word;
        /*过滤空格, 如果输入的不是 空, 就继续向下执行*/
        while(isspace(c = getc(stdin)));
        /*如果输入的不是结束符, 那么 wp指针, 先取值, 之后地址自增*/
        if(c != EOF)
                *wp++ = c;
        /*如果输入的不是字母, 直接返回, 关键字里面没有数字开头的*/
        if(!isalpha(c))
        {
                *wp = '\0';
                return c;
        }
        /*
         * 循环条件 a. 接收的最大字符个数 lim, 每读取一个字符, 该变量自减 
         * 当该变量自减为0时停止循环接收字符串
         * 循环条件 b. 当读取到的字符 不是 字母 或者数字的时候, 停止循环
         */
        for(; --lim > 0; wp++)
        {
                if(!isalnum(*wp = getc(stdin)))
                {
                        ungetc(*wp, stdin);
                        break;
                }
        }
        *wp = '\0';
        return word[0];
}
/*
 * 参数解析 : word 是要查找的字符串 tab 字符串数组 n 字符串大小
 */
int binsearch(char *word, struct key tab[], int n)
{
        /*
         * cond 判断 查找的字符串 是在mid 坐标之前 (<0) 之后(>0) 或者就是mid坐标
         * 
         * 如果查找的不是正好中间的变量符合, 就返回 -1 失败
         */
        int cond, low, high, mid;
        low = 0;
        high = n - 1;
        /*
         * 要找的值的下标在low 和 high之间
         * mid 是 low 和 high 的中值
         * 如果 word 小于中值下标 将比较范围 缩小
         */
        while(low <= high)
        {
                mid = (low + high) / 2;
                if((cond = strcmp(word, tab[mid].word)) < 0)
                        high = mid - 1;
                else if(cond > 0)
                        low = mid + 1;
                else
                        return mid;
        }
        return -1;
}



执行结果 :



[root@ip28 struct]# gcc word_count.c 
[root@ip28 struct]# ./a.out 
auto
break
break
char
 1 auto
 2 break
 1 char



宏定义方法 : 获取结构体数组大小;


-- sizeof 方法 : sizeof (对象) | sizeof (类型名称) 可以获取对象 或者 类型占用的存储空间, 其返回值是 size_t 类型的, 定义在stddef.h 头文件中;


-- 使用类型测量 :



#define KEYS (sizeof(key_count) / sizeof(struct key))

-- 使用对象测量 :


#define KEYS (sizeof(key_count) / sizeof(struct key_count[0])





4. 指向结构体指针





(1) 使用指针方式实现上面的关键字统计程序



使用指针进行二分查找 :


-- 使用下标找中值 : 在之前找中值时通过 mid = (low + high)方法, 这样做可行是因为 low 从0开始的;


-- 如果是指针情况 : mid low high 都是指针, 那么就不能使用上面的那种方法了, 使用 mid = low + (high - low) / 2;.


-- 指针操作情况的 high 和 low : 其中 low 是首元素的 首地址, high 是 尾元素的尾地址, 只有这样 它们的差的 0.5 倍才是准确的中值的首地址;




指针指向数组注意点 : 不要生成非法的指针, 指针不能指向数组之外的元素;


-- &key_count[-1] : 这个指针时非法的;


-- &key_count[n] : 对数组的最后一个元素后面的第一个元素进行 & 运算时合法的, 其它操作都是非法的;




示例程序 :



/*************************************************************************
    > File Name: pointer_struct.c
    > Author: octopus
    > Mail: octopus_work.163.com 
    > Created Time: Tue 25 Mar 2014 12:31:08 AM CST
 ************************************************************************/
#include<stdio.h>
#include<ctype.h>
#include<string.h>
#define MAXWORD 20
/*计算结构体数组的大小*/
#define KEYS (int)(sizeof(key_count) / sizeof(struct key))
struct key 
{
    char *word;
    int count;
} key_count[] = 
{
    "auto", 0,
    "break", 0,
    "case", 0,
    "char", 0,
    "const", 0
};
int getword(char *, int);
struct key *binsearch(char*, struct key*, int);
int main(int argc, char **argv)
{
    char word[MAXWORD];
    struct key *p; /*存放查找方法返回的结构体指针, 该指针指向数组中查找到元素的下标*/
    while(getword(word, MAXWORD) != EOF)
  if(isalpha(word[0]))
    if((p = binsearch(word, key_count, KEYS)) != NULL)
    p->count++;
    for(p = key_count; p < key_count + KEYS; p++)
  if(p->count > 0)
    printf("%2d %s \n", p->count, p->word);
    return 0;
}
/*
 * 没有循环控制变量的 for 循环, 在内部通过条件 break
 */
int getword(char *word, int max)
{
    int c, getc(FILE*), ungetc(int, FILE*);
    char *wp = word;
    /*处理第一个字符, 第一个字符不是 空 不是 EOF 再进行下面的操作*/
    while(isspace(c = getc(stdin)));
    if(c != EOF)
  *wp++ = c;
    if(!isalpha(c))
    {
  *wp = '\0';
  return c;
    }
    /*循环接收字符串, 字符串接收到非标识符 或者 到达个数上限停止循环*/
    for(; --max > 0; wp++)
  if(!isalnum(*wp = getc(stdin)))
  {
    ungetc(*wp, stdin);
    break;
  }
    *wp = '\0';
    return word[0];
}
/*
 * 注意点 : 
 *   取两个地址的中值 : 一个数组n个元素, 其中值计算 是由 首元素的首地址 和 尾元素的尾地址计算的
 *   二分查找 : 
 *     如果要把区间前移, 那么就需要将尾地址设置为 中间元素前一个元素的尾地址, 即中间元素的首地址
 *     如果要把区间后移, 那么就需要将首地址设置为 中间元素后一个元素的首地址, 即中间元素 + 1 的地址
 *
 * 指向结构体数组的指针 : 
 *   struct key tab * 是指向结构体数组指针, 该指针可以操作结构体数组
 */
struct key *binsearch(char *word, struct key *tab, int n)
{
    int cond;
    struct key *low = &tab[0];  /*0元素的首地址*/
    struct key *high = &tab[n]; /*尾元素的尾地址*/
    struct key *mid;
    while(low < high)
    {
  /*计算中间值的地址*/
  mid = low + (high - low) / 2;
  if((cond = strcmp(word, mid->word)) < 0)
    high = mid; /*mid 是 中间元素前一个元素的尾地址*/
  else if(cond > 0)
    low = mid + 1; /*这里low要成为mid后一个元素的首地址*/
  else
    return mid;
    }
    return NULL;
}



执行结果 :


octopus@octopus-Vostro-270s:~/code/c/struct$ gcc pointer_struct.c 
octopus@octopus-Vostro-270s:~/code/c/struct$ ./a.out 
auto
case
auto
break
 2 auto 
 1 break 
 1 case




(2) 结构体大小讨论





结构体数组指针算术运算 : struct key *p = word_count; 指针 p 指向 结构体数组, 如果 p + 1 , 结果是 p 地址 加上 结构体所占内存大小;




结构体大小 : 结构体的大小不是完全等于各个成员的长度之和, 对象之间有对齐要求;


-- 空穴 : 对象间对齐, 会产生空穴, 占有空间, 但是不存储数据;




示例 : 结构体中由一个 char 和 int , 占用的空间却是 8个字节, 它们的和是 5个字节;



/*************************************************************************
    > File Name: memory_struct.c
    > Author: octopus
    > Mail: octopus_work.163.com 
    > Created Time: 2014年03月25日 星期二 12时55分45秒
 ************************************************************************/
#include<stdio.h>
struct word
{
    char c;
    int i;
};
int main(int argc, char **argv)
{
    printf("sizeof(struct word) = %d \n", sizeof(struct word));
    return 0;
}

执行结果 :


octopus@octopus-Vostro-270s:~/code/c/struct$ gcc memory_struct.c 
octopus@octopus-Vostro-270s:~/code/c/struct$ ./a.out 
sizeof(word) = 8
上一篇:【嵌入式开发】gcc 学习笔记(一) - 编译C程序 及 编译过程(一)


下一篇:【C 语言】结构体相关 的 函数 指针 数组(一)