rn_xtcxyczjh-2 通用+回调2 [查找最大值、求和 访问字符串(进程虚拟存储空间.段)]
来源:互联网 发布:端口号 53 编辑:程序博客网 时间:2024/05/29 07:08
2015.09.19 – 09.20
读xtcxyczjh(系统程序员成长计划)—- 学习程序设计方法。
本次笔记基于“rn_xtcxyczjh-I 功能 封装 通用 回调“中y15m914的代码。笔记中的表达极差。笔记基于的源代码保存地址为:pxtcxyczjh-SourceII。
2015.09.19 - 查找 求和
需求简述。
对一个存放整数的双向链表,找出链表中的最大值。
对一个存放整数的双向链表,累加链表中所有整数。
准备。
用回调函数的方法实现需求:双向链表提供遍历链表的功能(分内),找最大值和求和的功能留给用户自己编写(分外)。
代码。
指定(用户编写的)双向链表求和回调函数类型。
参数:双向链表节点中的元素(void );避免使用全局变量的额外参数(void )。
返回值:void。
I 累积和
dlist.h(在dlist.h中定义双向链表值求和回调函数的类型)。
typedef void (*pCallbackDlistVisitFunT)(void *ctx, void *data);
为使定义含义更明显,使用形参名(ctx为避免使用全局变量的额外参数,data为指向双向链表中数据的指针)。
dlist.c(在dlist.c中编写遍历双向链表节点(时调用双向链表求和的回调函数)的接口)。
/*dlist.c*///...... /* Visit every node of dlist */int dlist_foreach(pDlT pdl, pCallbackDlistVisitFunT pvisit, void *ctx){ unsigned int i, len; if (NULL == pdl || NULL == pvisit) return -1; len = pdl->num; pnd = pdl->pnd; for (i = 0; i < len; ++i) { pvisit(ctx, pnd->pe); pnd = pnd->pn; } if (0 == i) return -1; return 0;}
在dlist.h中声明dlist_foreach。
/*dlist.h*///...... int dlist_foreach(pDlT pdl, pCallbackDlistVisitFunT pvisit, void *ctx);
用户编写回调函数。
在其它文件如main.c中编写双向链表求和的回调函数。
/* Sum the integer data of dlist */static void sum_data2ctx(void *ctx, void *data){ long int *sum; sum = (long int *)ctx; *sum += *data;}
用户所有的回调函数暂时都只会在main中被调用,所以可将所有的回调函数都限制为static(避免全局函数名污染全局名字空间,造成重名等问题)。
改写dlist.c中的create_dlist()函数,让双向链表中的数据全为整型。
/*dlist.c*///……static int itmp_data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0};//……/* Create len nodes for double linked list */pDlT create_dlist(unsigned int len){ //…… pf->pp = pn; pn->pn = pf; pn->pe = &itmp_data[i-1];//原来的语句为pn->pe = ctmp_data; //……}
改写main函数,对双向链表求和。
/*main.c*//* Entry of C program */int main(void){ pDlT pdl; pNdT pnd; int i, len = 10; long int sum = 0; pdl = create_dlist( len ); if (NULL == pdl) return -1; dlist_foreach(pdl, sum_data2ctx, &sum); printf("Sum of dlist-I:%ld\n", sum); for (i = 1; i <= len; ++i) { pnd = alloc_node(); if (NULL == pnd) { free_dlist(pdl); return -1; } insert_node2dlist(pdl, pnd, i); } for (i = 1; i <=len; ++i) { delete_node8dlist(pdl, i); } show_dlist(pdl, main_callback_show_dlist); free_dlist(pdl); return 0;}
修改dlist.c中显示双向链表元素的回调函数,让其正确显示链表内容。
/* dlist.c*/static void dlist_callback_show_dlist(pDlT pdl){ pNdT pnd; unsigned int i, len; len = pdl->num; pnd = pdl->pnd; for (i = 0; i < len; ++i) { printf("%d ", *((int *)(pnd->pe)) ); pnd = pnd->pn; } //printf("%s", ((char *)(pnd->pe)) ); printf("\n");}
在linux终端编译、运行程序。
II 查找最大值回调函数
到了此时,只需要在main.c中定义一个pCallbackDlistVisitFunT类型的函数,然后在函数内完成查找链表最大值功能即可。
/* Get the max number from dlist */static void max_data8dlist(void *ctx, void *data){ int *max, a, b; max = (int *)ctx; a = *max; b = *((int *)data); *max = a > b ? a : b;}
*ctx的初始值为双向链表的第一个元素。在main.c中访问不到双向链表的元素,故而在dlist.c中添加一个返回双向链表第i个元素的函数。
/* Get the i-th element of dlist */void * get_dlist_ith_elmt(pDlT pdl, unsigned int i){ int len; pNdT pnd; if (NULL == pdl) return NULL; len = pdl->num; if (0 == len) return NULL; pnd = pdl->pnd; for (i = 1; i < len; ++i) pnd = pnd->pn; return pnd->pe;}
并将get_dlist_ith_elmt函数声明在dlist.h中。
/*dlist.h*/void * get_dlist_ith_elmt(pDlT pdl, unsigned int i);
在main.c中测试求双向链表最大值的回调函数。
/* main.c *///……/* Entry of C program */int main(void){ pDlT pdl; pNdT pnd; int i, max, len = 10; long int sum = 0; pdl = create_dlist( len ); if (NULL == pdl) return -1; // 获取双向链表中所有元素的和 dlist_foreach(pdl, sum_data2ctx, &sum); printf("Sum of dlist-I:%ld\n", sum); // 获取双向链表的第一个元素给max,然后找双向链表中的最大值 max = *(int*)get_dlist_ith_elmt(pdl, 1); dlist_foreach(pdl, max_data8dlist, &max); printf("The max value-I:%d\n", max); //…… return 0;}
在linux终端编译运行程序。
源代码目录。
../xtcxyczjh/y15m9d19/
2015.09.20 – 字符串大小写转换
需求简述。
对一个存放字符串的双向链表,把存放在其中的字符串转换成大写字母。
准备。
字符串转换的功能不属于双向链表分内之事 + 处理双向链表的通用性,采取用dlist_foreach()函数回调用户编写的将字符串转换成大写字母的函数lstr2ustr()。在站在调用者的调度来编写回调函数lstr2ustr()。之前先修改一个问题:双向链表的值只能在dlist.c中的create_dlist()函数中指定,调用者并不能决定每个链表中的值。现在修改双向链表的相关接口,然调用者决定往链表存什么值。
在创建双向链表时将双向链表中的所有元素都循环初始化为1,2, …, 0。
/* dlist.c *///……pDlT create_dlist(unsigned int len){ //… //For doubly linked list node default static int itmp_data[] = {1, 2, 3, 4, 5, 6, 7, 8, 9, 0}; int ID_SIZE = 10; //…… for (i = 1; i < len; ++i){ //…... pp->pe = &itmp_data[(i - 1) % ID_SIZE]; //…... } //…… pn->pe = &itmp_data[(i - 1) % ID_SIZE]; //…… return pdl;}
将双向链表默认初值放在函数内部,避免全局变量。编译、运行程序,跟先前一样的结果。
然后提供一个可以初始化双向链表中任意一个节点的函数。
/* dlist.c *///….../* Assign i-th node */int assign_ith_node_value(pDlT pdl, void *data, unsigned int i){ pNdT pnd; unsigned int k, len; if (NULL == data) return -1; len = pdl->num; if (len < 1 || i < 1 || i > len) return -1; pnd = pdl->pnd; for (k = 1; k < i; ++k) pnd = pnd->pn; pnd->pe = data;}
在dlist.h中声明此函数。
在main函数中将双向链表初始化为字符串序列。
/* main.c */#include "dlist.h"#include <stdio.h>#include <ctype.h>#include <string.h>#include <stdlib.h>static void main_callback_show_dlist(void *data, unsigned int len);static void sum_data2ctx(void *ctx, void *data);static void max_data8dlist(void *ctx, void *data);#define DLIST_SIZE 10/* Entry of C program */int main(void){ pDlT pdl; //pNdT pnd; char *pstr[DLIST_SIZE]; int i, j, max, slen, len = DLIST_SIZE; long int sum = 0; char *str[DLIST_SIZE] = {"i", "love", "you", "once", "i", "love", "you", "twice", "i", "love"}; pdl = create_dlist( len ); if (NULL == pdl) return -1; // 获取双向链表中所有元素的和 dlist_foreach(pdl, sum_data2ctx, &sum); printf("Sum of dlist-I:%ld\n", sum); // 获取双向链表的第一个元素给max,然后找双向链表中的最大值 max = *(int*)get_dlist_ith_elmt(pdl, 1); dlist_foreach(pdl, max_data8dlist, &max); printf("The max value-I:%d\n", max); // 将字符串载入RAM中 for (i = 0; i < DLIST_SIZE; ++i){ slen = strlen(str[i]); pstr[i] = (char *)malloc(slen + 1); if (NULL == pstr[i]) { for (j = i - 1; j >= 0; --j) free(pstr[j]); free_dlist(pdl); return -1; } memcpy(pstr[i], str[i], slen + 1); } // 将RAM中的字符串一次赋值给双向链表的节点 for (i = 0; i < len; ++i) assign_ith_node_value(pdl, pstr[i], i + 1); show_dlist(pdl, main_callback_show_dlist); for (i = 0; i < DLIST_SIZE; i++) free(pstr[i]); free_dlist(pdl); return 0;}/* Callback function for show_dlist() */static void main_callback_show_dlist(void *data, unsigned int len){ printf("%s ", ((char *)data) );}/* Sum the integer data of dlist */static void sum_data2ctx(void *ctx, void *data){ long int *sum; sum = (long int *)ctx; *sum += *((int *)data);}/* Get the max number from dlist */static void max_data8dlist(void *ctx, void *data){ int *max, a, b; max = (int *)ctx; a = *max; b = *((int *)data); *max = a > b ? a : b;}
在main程序中,str[i]指向的的内容.rodata段中,保存在.rodata中的字符串不允许被修改。所以要将str中的内容拷贝到RAM中。
在linux终端编译、运行程序。
另外,在创建新的节点时,插入节点的值可由调用者传入。
/* dlist.c */ //….../* Allocate one node of doubly linked list */pNdT alloc_node(void *data){ pNdT pnd; pnd = (pNdT)malloc( sizeof(NDT) ); if (NULL != pnd) { pnd->pe = data; } return pnd;}
在dlist.h中更新alloc_node函数的声明。
现在可以来实现将双向链表中的字符串转换为大写的了。在main.c中编写回调函数lstr2ustr()。
/* main.c *///......#include <stdlib.h>//……/* Translate lower string to upper string */static void lstr2ustr(void *ctx, void *data){ char ch, *str; str = (char *)data; ch = *str; while (ch) { if (islower(ch)) *str = toupper(ch); ++str; ch = *str; }}
‘a’是一个字符常量,它的值在任何时候都 是97,但在不同语言中,97却不一定表’a’。我们不能简单的认为在97(‘a’)-122(‘z’)之间的字符就是小写字母,而是应该调用标准C函 数islower来判断,同样转换为大写应该调用toupper而不是减去一个常量。
回调函数lstr2ustr()的类型为pCallbackDlistVisitFunT。调用库函数来转换字符而不是用常量,这些函数在
/* main.c *///……int main(void){ //…… show_dlist(pdl, main_callback_show_dlist); // 将双向链表中的字符串转换为大写 dlist_foreach(pdl, lstr2ustr, NULL); show_dlist(pdl, main_callback_show_dlist); //….. Return 0;}
在linux终端编译、运行程序。
源代码目录。
../xtcxyczjh/y15m9d20/
一个Linux进程的虚拟存储空间。
来自《CSAPP》 2E-9.7.2
.bss
未初始化的全局变量(.bss段)用来存放那些没有初始化的和初始化为0的全局变量的。bss类型的全局变量只占运行时的内存空间,而不占用文件空间(可执行文件中记录了.bss段的大小)。
.data
初始化过的全局变量 (.data段)用来存放那些初始化为非零的全局变量。data类型的全局变量是即占文件空间,又占用运行时内存空间的。
.text
text段存放代码(如函数)和部分整数常量。
.rodata
rodata的意义同样明显,ro代表read only,rodata就是用来存放常量数据的。关rodata类型的数据,要注意以下几点:
(1)常量不一定就放在rodata里,有的立即数直接和指令编码在一起,存放在代码(.text)中。
(2)对于字符串常量,编译器会自动去掉重复的字符串,保证一个字符串在一个可执行文件(EXE/SO)中只存在一份拷贝。
(3)rodata是在多个进程间是共享的,这样可以提高运行空间利用率。
(4) rodata是在多个进程间是共享的,这样可以提高运行空间利用率。
(5) 在有的嵌入式系统中,rodata放在ROM(或者norflash)里,运行时直接读取,无需加载到RAM内存中。
(6) 在嵌入式linux系统中,也可以通过一种叫作XIP(就地执行)的技术,也可以直接读取,而无需加载到RAM内存中。
(7) 常量是不能修改的,修改常量在linux下会出现段错误。
这些机制由编译器(和操作系统)共定。
把在运行过程中不会改变的数据设为rodata类型的是有好处的:在多个进程间共享,可以大大提高空间利用率,甚至不占用RAM空间。同 时由于rodata在只读的内存页面(page)中,是受保护的,任何试图对它的修改都会被及时发现,这可以提高程序的稳定性。字符串会被编译器自动放到rodata中,其它数据要放到rodata中,只需要加const关键字修饰就好了。
Stack
栈用于存放临时变量和函数参数。根栈相关的笔记有“ 一个C源文件到可执行文件 [反汇编-函数栈帧 编译 链接]”、“ [Hb-XVII] 计算机的抽象层次-简 使用寄存器 使用内存空间 程序执行过程 使用main函数规定 不定参数函数机制 C”、“ [CSAPP-I] 过程(函数栈帧) C语句的机器级表示(gcc -S)”。
Heap
堆是最灵活的一种内存,它的生命周期完全由使用者控制。使用堆内存时请注意两个问题:
- malloc/free要配对使用。内存分配了不释放我们称为内存泄露(memory leak),内存泄露多了迟
早会出现Out of memory的错误,再分配内存就会失败。当然释放时也只能释放分配出来的
内存,释放无效的内存,或者重复free都是不行的,会造成程序crash。 - 分配多少用多少。分配了100字节就只能用100字节,不管是读还是写,都只能在这个范围
内,读多了会读到随机的数据,写多了会造成的随机的破坏。这种情况我们称为缓冲区溢出
(buffer overflow),这是非常严重的,大部分安全问题都是由缓冲区溢出引起的。
linux下检查内存泄露或缓冲区溢出的工具
valgrind
读《xtcxyczjh》-Part-II pnote over.
[2015.09.21-15:31]
- rn_xtcxyczjh-2 通用+回调2 [查找最大值、求和 访问字符串(进程虚拟存储空间.段)]
- rn_xtcxyczjh-1 功能 封装 通用 回调
- 进程地址空间与虚拟存储空间
- 进程地址空间与虚拟存储空间
- 浅谈进程地址空间与虚拟存储空间
- 浅谈进程地址空间与虚拟存储空间
- 浅谈进程地址空间与虚拟存储空间
- 浅谈进程地址空间与虚拟存储空间
- 进程地址空间与虚拟存储空间区别
- 字符串查找最大值问题
- 最大值求和(summax)
- 进程地址空间与虚拟存储空间的理解
- 进程地址空间与虚拟存储空间的理解
- 进程地址空间与虚拟存储空间的理解
- 进程地址空间与虚拟存储空间的理解
- 进程地址空间与虚拟存储空间的理解
- 进程地址空间与虚拟存储空间的理解
- [OS] 进程地址空间与虚拟存储空间的理解 很好!!!
- 中国 省市区县 数据库SQL 脚本
- 如何在App中实现朋友圈功能之七快速实现上拉加载朋友圈功能——箭扣科技Arrownock
- 20150921之前的信息泄露搜集-国内搜索引擎
- 计算机网络之TCP协议与UDP协议
- hdu acm2549
- rn_xtcxyczjh-2 通用+回调2 [查找最大值、求和 访问字符串(进程虚拟存储空间.段)]
- Linux下调试core dump文件的方法
- 2.WebService的开发手段
- 共享你的XMind6思维导图
- 设置div字体
- Android:EditText 多行显示及所有属性
- iOS后台保持(whose view is not in the window hierarchy)
- 风骚的Guard语法
- HDFS的快照原理和Hbase基于快照的表修复