C语言 结构体相关 函数 指针 数组
.
作者 : 万境绝尘
转载请注明出处 :http://www.hanshuliang.com/?post=30
.
结构体概述 : 结构体是 多个 变量的集合, 变量的类型可以不同;
-- 可进行的操作 : 结构体可以进行 拷贝 赋值操作, 可以作为 函数参数 和 函数返回值;
1. 结构体的基本使用
结构体声明 : struct 结构标记 {结构成员} 普通变量;
-- 结构体示例 :
struct student { char *name; int age; };
-- 结构标记 : struct 后面的 student 是结构标记, 这个标记 可写 可不写, 其作用是 为结构命名, 结构标记可以代表 {} 中的声明的所有的成员变量;
-- 结构成员 : 在 {} 中定义的变量就是结构成员;
-- 普通变量 : 在声明结构体的时候后面可以加上若干普通变量, 相当于定义结构体变量;
结构体变量声明 : 可以在定义的时候声明变量, 也可以在定义完结构体使用 结构标记 声明变量;
-- 定义结构体时声明变量 : 这种声明变量的方式可以不用 结构标记, 变量名写在 花括号 后面, 用头号隔开;
struct student { char *name; int age; } s1, s2, s3;
-- 使用结构标记声明 : 结构标记 student 代表了花括号的声明, 是 结构体的简写, 可以使用结构标记代替花括号中的内容;
struct student s4, s5, s6;
结构体内存分配 : 结构体内存是在声明变量的时候分配的, 如果只声明了结构体, 没有声明对应变量, 那么不会分配内存;
结构体变量初始化 :
-- 声明结构体的时候初始化 : struct student s1 = {"Tom", 12} ; 注意 初值表中必须时结构体对应类型的常量表达式;
-- 声明之后初始化 : 结构体变量名.成员名 可以访问结构体中的成员变量, s1.name = "Tom"; s2.age = 12;
结构体嵌套 : 结构体中的成员变量可以是 结构体变量;
struct student { char *name; int age; } s1; struct class { struct student s1; struct student s2; } c1;
结构体代码示例 :
/************************************************************************* > File Name: base_struct.c > Author: octopus > Mail: octopus_work.163.com > Created Time: 2014年03月24日 星期一 10时49分46秒 ************************************************************************/ #include<stdio.h> int main(int argc, char **argv) { /* * 声明结构体 同时声明变量s1 */ struct student { char *name; int age; } s1; /* * 结构体嵌套 */ struct class { struct student s1; struct student s2; } c1; struct student s2 = {"Tom", 12};/*只有声明的时候才能对结构体初始化才能使用花括号赋值*/ struct class c2 = {{"Jack", 13}, {"Pig", 15}}; s1.name = "Hack"; /*变量声明后对结构体赋值只能一个一个赋值*/ s1.age = 14; //s1 = {"fuck", 1}; /*只有在初始化的时候才能使用 花括号初始化结构体变量*/ c1.s1.name = "CJ"; c1.s1.age = 21; c1.s2.name = "KW"; c1.s2.age = 22; /*访问结构体中的变量, 使用 . 进行访问*/ printf("s1 : name = %s, age = %d \n", s1.name, s1.age); printf("s2 : name = %s, age = %d \n", s2.name, s2.age); printf("c1 : s1 : name = %s, age = %d ; s2 : name = %s, age = %d \n", c1.s1.name, c1.s1.age, c1.s1.name, c1.s2.age); printf("c2 : s1 : name = %s, age = %d ; s2 : name = %s, age = %d \n", c2.s1.name, c2.s1.age, c2.s1.name, c2.s2.age); return 0; }
执行结果 :
octopus@octopus-Vostro-270s:~/code/c/struct$ gcc base_struct.c octopus@octopus-Vostro-270s:~/code/c/struct$ ./a.out s1 : name = Hack, age = 14 s2 : name = Tom, age = 12 c1 : s1 : name = CJ, age = 21 ; s2 : name = CJ, age = 22 c2 : s1 : name = Jack, age = 13 ; s2 : name = Jack, age = 15
2. 结构体与函数
结构体的合法操作 :
-- 整体复制 : 结构体可以复制;
-- 整体赋值 : 声明结构体的时候可以整体赋值, 在其它情况下不可以;
-- & 取地址 : 使用 & 运算符获取 结构体地址;
-- 访问成员 : 使用 结构体变量名.成员变量名 可以访问成员变量;
函数传递结构体方法 :
-- 传递结构体成员 : 将结构体拆开, 将其中的成员变量分别传入;
struct class create_class(struct student s2, struct student s3) { struct class c1 = {s2, s3}; return c1; }
-- 传递结构体 : 将整个结构体当做参数传入, 这种情况和传递其它类型参数一样, 都是通过值传递的;
struct class create_class(struct student s2, struct student s3) { struct class c1 = {s2, s3}; return c1; }
-- 传递结构体指针 : 传递结构体的指针, 访问形式如下;
/* * 传入一个结构体指针 * 通过指针访问结构体的方法 : * (*结构体指针变量).成员变量 访问; * 结构体指针变量 -> 成员变量 访问; */ void printf_struct_pointer(struct student *s) { printf("student : (*s).name = %s, (*s).age = %d \n", (*s).name, (*s).age); printf("student : s->name = %s, s->age = %d \n", s->name, s->age); }
结构体指针访问 :
-- 示例 : 定义一个结构体指针;
struct student { char *name; int age; }*p;
-- "." 和 "->"优先级 : 这两个运算符都是从左到右运算, 都是右结合; "." 和 "->" 优先级比 "*" , "++" 优先级要高; 这两个运算符 与 () [] 是四个优先级最高的运算符;
-- ++p->age 分析 : 是对结构体中的 age 进行自增操作;
-- *p->name 分析 : 获取 结构体中的 name 字符串的值(注意不是指针|地址);
-- *p++->name 分析 : 先获取 name 字符串的值, 再将p自增;
结构体函数示例 :
/************************************************************************* > File Name: method_struct.c > Author: octopus > Mail: octopus_work.163.com > Created Time: 2014年03月24日 星期一 14时46分16秒 ************************************************************************/ #include<stdio.h> /*声明一个结构体类型, 其成员变量是普通变量*/ struct student { char *name; int age; }; /*声明一个结构体类型, 其成员变量是结构体*/ struct class { struct student s1; struct student s2; }; /* * 传递 2 个结构体的成员变量 * 在函数中创建结构体并返回 */ struct student create_student(char *name, int age) { struct student s1 = {name, age}; return s1; } /* * 传递 2 个结构体变量 */ struct class create_class(struct student s2, struct student s3) { struct class c1 = {s2, s3}; return c1; } /* * 传入一个结构体指针 * 通过指针访问结构体的方法 : * (*结构体指针变量).成员变量 访问; * 结构体指针变量 -> 成员变量 访问; */ void printf_struct_pointer(struct student *s) { printf("student : (*s).name = %s, (*s).age = %d \n", (*s).name, (*s).age); printf("student : s->name = %s, s->age = %d \n", s->name, s->age); } int main(int argc, char **argv) { /*使用函数获取一个结构体, 传入结构体的值*/ struct student s1 = create_student("Tom", 11); printf("student : name = %s, age = %d \n", s1.name, s1.age); /*创建一个成员变量是结构体的结构体, 并打印结构体数据*/ struct class c1 = create_class(create_student("Jack", 12), create_student("CJ", 13)); printf("c1 : s1 : name = %s, age = %d ; s2 : name = %s, age = %d \n", c1.s1.name, c1.s1.age, c1.s2.name, c1.s2.age); /*声明结构体指针*/ struct student *p = &s1; printf_struct_pointer(p); return 0; }
执行结果 :
octopus@octopus-Vostro-270s:~/code/c/struct$ gcc method_struct.c octopus@octopus-Vostro-270s:~/code/c/struct$ ./a.out student : name = Tom, age = 11 c1 : s1 : name = Jack, age = 12 ; s2 : name = CJ, age = 13 student : (*s).name = Tom, (*s).age = 11 student : s->name = Tom, s->age = 11
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
.
作者:万境绝尘
转载请注明出处:http://www.hanshuliang.com/?post=30
.
相关推荐
11. **C语言结构体与共用体**:结构体允许创建复合数据类型,而共用体则在有限的内存空间内存储多种类型的数据。这两者在实现数据结构如链表、树等时非常有用。 12. **C语言函数**:函数是组织代码的基本单元,可以...
在C语言编程中,循环、嵌套、数组、指针、函数和结构体是核心概念,它们构成了C语言的强大功能和灵活性。以下是对这些概念的详细解释和实践应用。 一、循环 C语言中的循环主要有三种类型:for循环、while循环和do-...
"C语言指针数组函数笔记" ...本笔记总结了C语言中的指针和数组相关知识点,涵盖了变量、指针变量、指针运算符、数组和指针数组函数等内容。通过阅读本笔记,读者可以更好地理解和应用C语言中的指针和数组。
17. 函数指针数组:在C语言中,函数指针数组可以用于实现函数指针的数组化。函数指针数组需要注意函数指针数组的定义和使用方法。 18. 宏定义数组:在C语言中,宏定义数组可以用于实现数组的宏定义。宏定义数组需要...
《C语言之指针、数组和函数》 在C语言中,指针、数组和函数是三个核心概念,它们在程序设计中起着至关重要的作用。指针是C语言的精髓之一,它允许我们直接访问内存地址,进行高效的数据操作。而数组则是一种存储...
C语言结构体部分内容详解包括结构体定义及使用,结构体函数的定义及使用,指向结构体的指针
在C语言中,结构体(struct)是一种复合数据类型,它允许我们将多个不同类型的变量组合成一个单一的实体。这使得我们能够创建更复杂的数据结构,以更好地模拟现实世界中的对象或概念。以下是对“C语言 结构体范例...
C语言结构体的用法 结构体是一种复杂的数据类型,在C语言中经常使用。结构体的定义、使用和操作都是C语言程序设计中的重要知识点。 一、 结构体类型定义 结构体类型定义是指定义一个结构体的组织形式,但不分配...
"C语言结构体共2页.pdf.zip"可能包含关于结构体的基本概念、定义、初始化、指针操作以及结构体数组和结构体指针的使用等内容。下面将详细介绍这些知识点。 1. **结构体定义**: 在C语言中,我们可以通过`struct`...
此外,结构体还可以包含函数指针,这样的设计允许更灵活的数据和行为的组合。 五、空间换时间:结构体中成员变量的对齐之道 由于硬件平台的限制,某些数据类型的访问速度可能不如其他类型,比如访问8字节的数据...
【C语言-数组、结构体与指针的综合应用实验报告】 本次实验旨在掌握C语言中数组、结构体和指针的基本用法,以及初步理解链表的概念和操作。实验内容涉及设计一个结构体来存储学生信息,包括学号、姓名、三科成绩和...
### C语言结构体、联合体、指针等 #### 一、结构体的深入理解 **1.1 结构体的概念** 结构体(`struct`)是C语言中的一种复合数据类型,它允许开发者将不同类型的数据组织在一起形成一个新的数据结构。这种特性极...
指针面试题精华 以下是对指针面试题的知识点总结: ...这些知识点涵盖了C语言中指针的各种使用场景,包括结构体指针、数组指针、多维数组指针、指针数组、函数指针、指针数组的指针、函数指针数组和字符指针等。
在C语言中,虽然无法直接返回数组,但通过返回数组指针或使用结构体的方式仍然能够有效地解决这个问题。每种方法都有其适用场景,开发者可以根据实际需求选择最合适的方法。需要注意的是,对于动态分配的内存,必须...
本资源摘要信息涵盖了C语言结构体的基本概念、定义、嵌套定义、结构变量的定义和初始化、结构成员的引用、结构数组的定义和使用、结构指针的使用等知识点。 1. 结构体的概念和定义 结构体是一种新的构造数据类型,...
第七章 数组 一、选择题 1.有两个字符数组a、b,则以下正确的输入语句是_______. A、gets(a,b); B、scanf("%s%s",a,b); C、scanf("%s%s",&a,&b); D、gets("a"),gets("b"); 2.下面程序段的运行结果是_________. ...
结构体指针也可以作为函数参数,使得函数能对传入的结构体进行操作,而无需复制整个结构体。 总的来说,结构体在C语言中是非常重要的工具,它扩展了数据表示的灵活性,使得我们能更准确地描述复杂的数据关系,提高...
在本文中,我们介绍了C语言中的可变数组和单链表两种数据结构,并实现了相关的算法。可变数组可以动态增加或减少大小,链表可以动态添加或删除节点。这些数据结构和算法可以广泛应用于各种软件开发领域。
在这个案例中,系统使用了两种不同的方法来实现:结构体数组法和链表法,这两种都是在C语言和C++中处理数据的有效方式。下面将详细讨论这两个方法以及相关的编程知识点。 1. **结构体数组法**: 在`学生成绩管理...