C语言08字符串 & 预处理 & 结构体

项目开发中字符串模型建立

strstr的while dowhile模型

//int cltClient_rev(void *handle, unsigned char *buf, int *buflen)

//不要相信别人给你传送的内存地址是可用的

int getCout(char *str, char *substr, int *count)

{

         int rv = 0;

         char *p = str;

        

         int ncout = 0;

         if (str==NULL || substr== NULL ||  count==NULL)

         {

                   rv = -1;

                   printf("func getCout()check (str==NULL || substr== NULL ||  count==NULL) err:%d \n" , rv);

                   return rv;

         }

 

         do

         {

                   p = strstr(p, substr);

                   if (p == NULL) //没有找到则跳出来

                   {

                            break;

                   }

                   else

                   {

                            ncout++;

                            p = p + strlen(substr);

                   }

 

         } while (*p != '\0');

 

         //fuzhi

         *count  = ncout;

        

         printf("ncout:%d\n", ncout);

         return rv;

 

}

 

void main36()

{

         char *p = "abcd1111abcd222abcd3333";

         int ncout = 0;

 

         while (p = strstr(p, "abcd"))

         {

                   p = p + strlen("abcd");

                   ncout ++;

                   if (*p == '\0')

                   {

                            break;

                   }

         }

         printf("ncout:%d\n", ncout);

         system("pause");

}

两头堵模型(两种写法)

 

//求去掉空格

//int trimSpaceStr2(char *p, unsigned char *buf2, int *buf2len)

int trimSpaceStr2( char *p, char *buf2)

{

         int ret = 0;

 

         int ncount = 0;

         int i, j;

         i = 0;

         j = strlen(p) -1;

 

         while (isspace(p[i]) && p[i] != '\0')

         {

                   i++;

         }

 

         while (isspace(p[j]) && j>0 )

         {

                   j--;

         }

 

         ncount = j - i + 1;

         //

         strncpy(buf2, p+i, ncount);

         buf2[ncount] = '\0';

         return ret;

}

 

//求去掉空格

//int trimSpaceStr2(char *p, unsigned char *buf2, int *buf2len)

//不要轻易去改变指针输入特性中in内存块的内存。。。。

int trimSpaceStr2_notgood( char *p)

{

         int ret = 0;

 

         int ncount = 0;

         int i, j;

         i = 0;

         j = strlen(p) -1;

 

         while (isspace(p[i]) && p[i] != '\0')

         {

                   i++;

         }

 

         while (isspace(p[j]) && j>0 )

         {

                   j--;

         }

 

         ncount = j - i + 1;

         //

         strncpy(p, p+i, ncount);

         p[ncount] = '\0';

         return ret;

}

字符串反转模型

 

void main51()

{

         char p[] = "abcde";

         char c ;

         char *p1 = p;

         char *p2 = p + strlen(p) -1;

 

         while (p1 < p2)

         {

                   c = *p1;

                   *p1 = *p2;

                   *p2 = c;

                   ++p1;

                   --p2;

         }

 

         printf("p:%s \n", p);

         system("pause");

 

}

 

 

两个辅助指针变量挖字符串  

 

int getKeybyValue(char *pKeyValude, char *pKey, char *pValude)

{

         char rv = 0;

         char *p = NULL;

 

         if (pKeyValude==NULL  )

         {

                   rv = -1;

                   printf("func getKeybyValue() err:%d pKeyValude \n", rv);

                   return rv;

         }

         if ( pKey==NULL )

         {

                   rv = -1;

                   printf("func getKeybyValue() err:%d pKey=NULL \n", rv);

                   return rv;

         }

         if ( pValude==NULL )

         {

                   rv = -1;

                   printf("func getKeybyValue() err:%d pValude \n", rv);

                   return rv;

         }

 

         //1 在pKeyValude中查找是否有关键字pKey

         p = strstr(pKeyValude, pKey);

         if (p == NULL)

         {

                   rv = -1;

                   printf("func getKeybyValue() err:%d 查找没有关键字pKey  \n", rv);

                   return rv;

         }

         p = p + strlen(pKey); //为下一次检索做准备

 

         //2 有没有=

         p = strstr(p, "=");

         if (p == NULL)

         {

                   rv = -2;

                   printf("func getKeybyValue() err:%d 查找没有=  \n", rv);

                   return rv;

         }

         p = p + 1; //为下一次提取valude做准备

        

         //3 提取按照要求的valude

         rv = trimSpaceStr03(p, pValude);

         if (rv != 0)

         {

                   printf("func trimSpaceStr03() err:%d \n", rv);

                   return rv;

         }

 

         return rv;

}




指针作函数参数输入模型



-------------------------------------------------------------------------
字符串
-------------------------------------------------------------------------
1.字符串基础:


#include "stdlib.h"
#include "stdio.h"
#include "string.h"


//int * char *
//c语言里面没有字符串这种类型。。。。。
//通过字符数组来模拟字符串
//C风格字符串是以零结尾的字符串
//
void main11()
{
//字符数组初始化
//指定长度 如果定义的长度剩余部分补充0
char buf1[100] = {'a', 'b', 'c'};
//不指定长度
char buf2[] = {'a', 'b', 'c'};
char buf3[] = {'a', 'b', 'c','\0'};


//通过字符串初始化字符数组 并且追加\0
char buf4[] = "abcdefg";


printf("%s\n", buf4 );


system("pause");
}


//sizeof
void main12()
{
//字符数组初始化
//指定长度 如果定义的长度剩余部分补充0
char buf1[100] = {'a', 'b', 'c'};
//不指定长度
char buf2[] = {'a', 'b', 'c'};
char buf3[] = {'a', 'b', 'c','\0'};


//通过字符串初始化字符数组 并且追加\0
char buf4[] = "abcd";


printf("%s\n", buf4 );
//注意sizeof是对数组类型进行大小测量 包括了\0
printf("sizeof(buf4): %d\n ", sizeof(buf4)); 
//strlen是求字符串的长度不包括\0
printf("strlen(buf4): %d \n", strlen(buf4));
system("pause");
}


//操作数组的方法
//下标法和指针法
void main()
{
int i = 0;
char *p = NULL;
//通过字符串初始化字符数组 并且追加\0
char buf4[] = "abcd";

for (i=0; i<strlen(buf4); i++)
{
printf("%c", buf4[i]); //p[]
}
//[] *的本质到底是什么?
//*p 是我们程序员手工的(显示)去利用间接赋值
//【】 只不过是,c/c++ 编译器帮我们做了一个*p的操作。。。。。。
// buf4[i]======> buf4[0+i] ====>  *(buf4+i)
//===*(buf4+i)   --> bu4[i];


printf("\n");
p = buf4;
for (i=0; i<strlen(buf4); i++)
{
printf("%c", *(p+i)); //*p 
}
system("pause");
}


2.自定义字符串拷贝基本模型


#include "stdlib.h"
#include "stdio.h"
#include "string.h"


//自定义:字符串copy函数,完成字符串from ,到to的copy
void copy_str1(char *from, char *to)
{
for (; *from!='\0'; from++, to++)
{
*to = *from;
}
*to = '\0';
}
void copy_str2(char *from, char *to)
{
while(*from!='\0')
{
*to = *from;
from++;
to++;
}
*to = '\0';
}


//++优先级高,但是++是后缀++
//所以先执行*to = *from; 再 from++; to ++from++;
void copy_str3(char *from, char *to)
{
while((*to++ = *from++))
{
;
}
}


void main()
{
//输入:
//在主调里函数分配内存
char *p = "abcdefg";       
char p2[100] ;//char *p2 = NULL;
//在被调函数里使用内存
copy_str3(p, p2);//strcpy(p2, "abcdeeg");
//输出:
printf("p2:%s\n", p2);
system("pause");
}


¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥
看见一级指针,要去分辨指针的输入输出特性                         ¥
指针的输入特性:在主调函数里面分配内存,在被调用函数里面使用      ¥
指针的输出特性:在被调用函数里面分配内存,主要是把运算结果甩出来  ¥
¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥¥


3.项目开发字符串模型(此处为指针输入特性)


//char *p = "abcd1111abcd222abcd3333" 请你找出字符串abcd出现的次数
//要求1:请自己定义一个接口(函数),并实现功能;70分
//要求2:编写测试用例。30分
/*
//输入:要查找的字符串
待查找的子串
输出的结果*/
//接口提示:int cltClient_rev(void *handle, unsigned char *buf, int *buflen)


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


//1级指针作函数参数(形参),用于修改0级指针(实参)
int getCout(char *str, char *substr, int *count)
{
int rv = 0;//用于输出检查和函数返回
char *p = str;//定义一个指针,用于接收形参地址
int ncout = 0;//用于计数
//不能相信别人传过来的地址就一定可用,需作安全检查
if (str==NULL || substr== NULL ||  count==NULL)
{
rv = -1;//地址不可用
printf("func getCout()check  err:%d \n" , rv);
return rv;
}


do 
{
p = strstr(p, substr);//此处调用的是库函数
if (p == NULL) //没有找到则跳出来
{
break;
}
else 
{
ncout++;//找到了就,计数加1
p = p + strlen(substr);//地址向后移动待查字符串个长度
}


} while (*p != '\0');//目标字符串遍历完成就结束循环

*count  = ncout;//取实参地址间接修改实参
printf("ncout:%d\n", ncout);


return rv;//函数返回(int类型)
}


void main()
{
int ret = 0;//用于接收接口函数的返回值
int ncout = 0;//用于计数(第3个参数)
//输入:在主调函数里分配内存
  /*目标字符串(第1个参数)*/
char *p = "abcd1111abcd222abcd3333";//分配了内存,可得到计数值
//char *p = NULL;//没有分配内存,没有计数值
  /*待查字符串(第2个参数)*/
char *subp = "abcd";
//输出:在被调函数里使用内存
ret = getCout(p, subp, &ncout);
//安全检查
if (ret != 0)
{
printf("func getCout() err:%d \n", ret);
return ;
}
printf("coutn:%d \n", ncout);
system("pause");


}
//下面这样的代码很不OK!!!
void main01()
{
char *p = "abcd1111abcd222abcd3333";
int ncout = 0;
while (p = strstr(p, "abcd"))
{
p = p + strlen("abcd");
ncout ++;
if (*p == '\0')
{
break;
}
}
printf("ncout:%d\n", ncout);
system("pause");
}


4.两头堵模型


#include "stdio.h"
#include "stdlib.h"
#include "string.h"
//不成熟的做法
void main01()
{
int count = 0;
int i = 0, j = 0;


char *p = "   abcd       ";
j = strlen(p) -1;


while (isspace(p[i]) && p[i] != '\0')
{
i++;
}


while (isspace(p[j]) && j>0)
{
j--;
}
count = j-i +1;

printf("count:%d", count);


system("pause");
}


//一级指针的输入模型,没有内存就没有指针


/*int trimSpace_很不ok的做法(char *mybuf)
{
int count = 0;
int i = 0, j = 0;
char *p = mybuf;
j = strlen(p) -1;
while (isspace(p[i]) && p[i] != '\0')
{
i++;
}
while (isspace(p[j]) && j>0)
{
j--;
}
count = j-i +1;
printf("count:%d", count);
//void *  __cdecl memcpy(void *, const void *, size_t);
memcpy(mybuf, mybuf+i, count);
mybuf[count] = '\0';
return 0;
//system("pause");
}
*/


//一般情况下不要修改输入的内存块的值


int trimSpace_ok(char *mybuf, char *outbuf)
{//此函数去掉字符串前后空格
int count = 0;
int i = 0, j = 0;


char *p = mybuf;//定义一个指针接收形参地址
j = strlen(p) -1;
//isspace()函数用于检测字符串是否为空
while (isspace(p[i]) && p[i] != '\0')
{
i++;
}


while (isspace(p[j]) && j>0)
{
j--;
}
count = j-i +1;

printf("count:%d\n", count);
//void *  __cdecl memcpy(void *, const void *, size_t);
memcpy(outbuf, mybuf+i, count);
outbuf[count] = '\0';
return 0;
}


void main()
{
int ret = 0;//用于检查和接收函数返回至
char *p = NULL;
char buf2[100];


//对于字符串分配内存有三种方式,可以在堆、栈、全局区(常量区),
//你要知道你的内存是怎么分配的
char *buf = "       abcd11111abcd2222abcdqqqqq       "; //常量区
//char buf[] = "       abcd11111abcd2222abcdqqqqq       ";//栈


/*很不OK的做法
ret = trimSpace(buf);
if (ret != 0)
{
printf("func trimSpace() err:%d\n", ret);
return ;
}
*/


ret = trimSpace_ok(buf, buf2);
if (ret != 0)
{
printf("func trimSpace() err:%d\n", ret);
return ;
}
printf("buf2:%s \n", buf2);
system("pause");


}


5.字符串反转模型


#include "stdlib.h"
#include "stdio.h"
#include "string.h"


void main()
{
char p[] = "abcde";//分配内存
char c ;
char *p1 = p;//指向字符串头
char *p2 = p + strlen(p) -1;//指向字符串尾


while (p1 < p2)//如果p1<p2,将其所指向的字符交换
{
c = *p1;
*p1 = *p2;
*p2 = c;
++p1;
--p2;//++、--可写到交换语句中
}
printf("p:%s \n", p);
system("pause");


}


6.两个辅助变量挖字符串


/*
有一个字符串符合以下特征(”abcdef,acccd,eeee,aaaa,e3eeeee,sssss,";)
要求写一个函数(接口),输出以下结果:
1) 以逗号分割字符串,形成二维数组,并把结果传出;
2) 把二维数组行数运算结果也传出。
strchr(“aa,aa”,’,’ );


请自己定义一个接口(函数)。
要求1:能正确表达功能的要求,定义出接口(函数)(30分);
要求2:正确实现接口(函数),并实现功能(40分);
要求3:编写正确的测试用例。(30分)。
*/
//abcdef,acccd,eeee,aaaa,e3eeeee,sssss,
/*
abcdef
acccd 
eeee,
aaaa,
e3eeeee,
sssss,
*/


#include "stdio.h"
#include "string.h"
#include "stdlib.h"


int spitString(const char *buf1, char c, char buf[10][30], int *num)
{
int ret = 0;
char *p = NULL;
char *pTmp = NULL;
int ncount = 0;
if (buf1==NULL ||  num==NULL)
{
return -1;
}
//步骤1 初始化条件 pTmp,p都执行检索的开头
p = buf1;
pTmp = buf1;
do 
{
//步骤2 strstr strchr,会让p后移 在p和pTmp之间有一个差值
p = strchr(p, c);
if (p == NULL) //没有找到则跳出来
{
break;
}
else 
{
//挖字符串
strncpy(buf[ncount], pTmp, p-pTmp);
buf[ncount][p-pTmp] = '\0';


ncount++;


//步骤3 让p和pTmp重新初始化,达到检索的条件
pTmp = p  = p + 1;
}


} while (*p != '\0');
//printf("ncout:%d\n", ncount);
*num = ncount;
return ret;
}
void main()
{
int ret = 0, i = 0;
const char *buf1 = "abcdef,acccd,eeee,aaaa,e3eeeee,sssss,";
char c = ',';
char buf[10][30];
int num = 0;
ret =  spitString(buf1, c, buf, &num);
if (ret != 0)
{
printf("func spitString() err:%d\n", ret);
return ret;
}


for (i=0; i<num; i++)
{
printf("%s\n", buf[i]);
}


system("pause");
}


7.二级指针第一种内存模型模型(指针数组):


在栈中的一维数组保存的地址指向常量区分配的内存


#include "stdio.h"
#include "stdlib.h"
#include "string.h"
//二级指针作函数参数修改一级指针的值
int printfArr(char **ArrayStr, int iNum)
{
int i = 0;
for (i=0; i<iNum; i++)
{
printf("%s \n", ArrayStr[i]);
}
return 0;
}


int sortArrayStr(char **ArrayStr, int iNum)
{
int i = 0, j = 0;
char *tmp = NULL;//用于交换是的临时变量,由于C语法规定,必须在语句前定义
//排序
for (i=0; i<iNum; i++)
{
for (j=i+1; j<iNum; j++)
{
if (strcmp(ArrayStr[i],ArrayStr[j]) > 0)
{
//一级指针交换(数组元素)
tmp = ArrayStr[i];
ArrayStr[i] = ArrayStr[j];
ArrayStr[j] = tmp;
}
}
}
return 0;
}


//二级指针第一种内存模型
void main()
{
//分配内存(常量区,不可修改)
char *ArrayStr[] = {"ccccc", "aaaa", "bbbb","11111"};


printf("排序之前\n");
printfArr(ArrayStr,4);


sortArrayStr(ArrayStr, 4);


printf("排序之后\n");
printfArr(ArrayStr,4);


system("pause");
}


8.二级指针第二种内存模型(二维数组):


在栈中分配(二维数组型)内存


#include "stdio.h"
#include "stdlib.h"
#include "string.h"


int printfArr22(char **ArrayStr, int iNum)
{
int i = 0;
for (i=0; i<iNum; i++)
{
printf("%s \n", ArrayStr[i]);
}
return 0;
}


//int array[10]===>int *array===>
// int printfArr22(char array[10], int iNum);
int printfArr23(char myArray[10][30], int iNum)
{
int i = 0;
for (i=0; i<iNum; i++)
{
printf("%s \n", myArray[i]);
}
return 0;
}


// int printfArr22(char array[10], int iNum);
int sortArr23(char myArray[10][30], int iNum)
{
int i = 0, j = 0;
char buf[30]; //buf数组名代表数组首元素的地址
//排序
for (i=0; i<4; i++)
{
for (j=i+1; j<4; j++)
{
if (strcmp(myArray[i], myArray[j]) > 0)
{
strcpy(buf, myArray[i]);
strcpy(myArray[i],myArray[j]);
strcpy(myArray[j], buf);
}
}
}
}


void main()
{
int i = 0;
char myArray[10][30] =  {"ccccc", "aaaa", "bbbb","11111"}; 


//打印第二种内存模型
for (i=0; i<4; i++)
{
printf("%s \n", myArray[i]);
}


printf("第二种内存模型,排序之前\n");
printfArr23(myArray, 4);
//printfArr23(myArray[10][30], 4);


sortArr23(myArray, 4);


printf("第二种内存模型,排序之后\n");
printfArr23(myArray, 4);
system("pause");
}


9.二级指针第三种内存模型(手工二维内存、二级指针):


在栈中的指针ArrayStr指向堆中分配的(指针数组型)内存


#include "stdio.h"
#include "stdlib.h"
#include "string.h"


int printfArr33(char **ArrayStr, int iNum)
{
int i = 0;
for (i=0; i<iNum; i++)
{
printf("%s \n", ArrayStr[i]);
}
return 0;
}


int printfArr2_23(char myArray[10][100], int iNum)
{
int i = 0;
for (i=0; i<iNum; i++)
{
printf("%s \n", myArray[i]);
}
return 0;


}


int sortArrayStr03(char **ArrayStr, int iNum)
{
int i = 0, j = 0;
char *tmp = NULL;
//排序
for (i=0; i<iNum; i++)
{
for (j=i+1; j<iNum; j++)
{
if (strcmp(ArrayStr[i],ArrayStr[j]) < 0)
{
tmp = ArrayStr[i];
ArrayStr[i] = ArrayStr[j];
ArrayStr[j] = tmp;
}
}
}
return 0;
}


void main()
{
int i = 0, j = 0;
char buf[100];
char **myarray = (char **)malloc(10*sizeof(char*)); //int array[10]
if (myarray == NULL)
{
return;
}
for (i=0; i<10; i++)
{
myarray[i] = (char *)malloc(100*sizeof(char)); //char buf[100];
if (myarray[i]  == NULL)
{
printf("ddddde\n");
return;
}
sprintf(myarray[i],"%d%d%d ", i, i, i);
}


//第三种内存模型打印 
printf("排序之前\n");
printfArr33(myarray, 10);
//printfArr2_23(myarray, 10); //第二种打印不适合 err


sortArrayStr03(myarray, 10);


//第三种内存模型打印
printf("排序之后\n");
printfArr33(myarray, 10);


for (i=0; i<10; i++)
{
free(myarray[i] );
}
if (myarray != NULL) 
{
free(myarray);
}


system("pause");
}




10.一维数组的本质(常量指针)


#include "stdio.h"
#include "stdlib.h"
#include "string.h"


void main()
{
int a;
int *p = NULL;
int i = 0;


//我声明了一个数组类型 (固定大小内存块的别名)
typedef int(MyArr5)[5];  
//定义一个指向数组类型的指针变量
MyArr5 *pArray;// &a;
{
int j = 0; 
int *pI = &j;
}
{
//int buf[10][30]
//a1代表数组首元素的地址(不是整个数组的地址),请问a1 指针变量
//1变量-->2指针变量--》 3常量指针变量 (常量指针)
//结论:不能被随便的改变指针变量的值(不能随便的改变指针的指向)
//为什么它是一个const  


//4在定义a1[5]的时候,编译器分配内存,为了能顺利的回收内存,为了有机会让编译器拿到原始内存首地址。
//编译器就把a1做成const量。
//不能深入的理解c指针各种现象,是产生bug的根源


int a1[5] = {1,3,4,55, 6};
//char *p = &a1;
//a1 = 0x11; 


//给数组指针赋值 需要。。。&a1
MyArr5 *pArray = &a1; //4个字节
//用数组指针去遍历数组
for (i=0; i<5; i++)
{
//a1[i] = i;
// = i;
printf("%d ", (*pArray)[i]);
}
}




{
//直接定义一个 数组指针类型 用这个类型定义指针变量
//我声明了一个数组类型 (固定大小内存块的别名)
typedef int(MyArr5_1)[5];  
//定义一个指向数组类型的指针变量
//声明一个 数组指针类型
typedef int (*PMyArr5)[5]; 
PMyArr5 myP = NULL;


int b[5] = {1,3,4,55, 6};


myP = &b;


for (i=0; i<5; i++)
{
//a1[i] = i;
// = i;
printf("%d ", (*myP)[i]);
}
}




{
int c[5] = {1,3,4,55, 6};
//定义一个指向数组的指针变量
int (*myparra)[5] = &c;
for (i=0; i<5; i++)
{
printf("%d ", (*myparra)[i]);
}
}
system("pause");
}


11.多(2)维数组的本质(数组指针)


#include "stdlib.h"
#include "string.h"
#include "stdio.h"


//证明二维数组的存储,是线性的
void printArray(char aa[][5]);
void printArray2(int *p);


void main()
{
int a[3][5];
int c[5]; //&c + 1;
int b[10]; //b代表数组首元素的地址 &b代表这个数组的地址 &b+1相当于 指针后移4*10个单位
//指针步长===》铁律1

//a代表什么什么那?a是一个数组指针 指向低维数组的指针
//a +1;
printf("a:%d, a+1:%d \n", a, a+1); //4*5


{
int i=0, j = 0, tmp = 0;
for (i=0; i<3; i++)
{
for (j=0; j<5; j++)
{
a[i][j] = ++tmp;
}
}


printf("\n");
for (i=0; i<3; i++)
{
for (j=0; j<5; j++)
{
printf("%d \n", a[i][j]);
}
}
}


//a的本质是一个数组指针。。。。每次往后跳一维的维数。。。。。。
{
int i = 0, j = 0;
//定义了一个数组指针 变量
int (*myArrayPoint)[5] ; //告诉编译给我开辟四个字节内存
myArrayPoint  =  a;
printf("\n");
for (i=0; i<3; i++)
{
for (j=0; j<5; j++)
{
//myArrayPoint[i][j] = ++tmp;
printf("%d \n", myArrayPoint[i][j]);
}
}
}


/*
char array[10][30];
(array+i) //相当于 第i行的首地址 //二级指针


(*(array+i))//一维数组的首地址


(*(array+i))+j //相当于第i行第j列的地址了把。。。。


*((*(array+i))+j) //相当于第i行第j列的地址了把。。。。<====>array[i][j]
*/
system("pause");
}


12.指针数组实例


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


//演示:指针数组的用法
//演示:找错误 注意return


//求关键字在表中的位置
//一个入口 多个出口
int searcheKeyTable(const char* table[], const int size, const char* key, int *pos)
{
int rv = 0;
int i = 0;
int inum = 0;
if (table==NULL || key==NULL || pos==NULL)
{
rv = -1;
printf("func searcheKeyTable:%d", rv);
return rv;
}
inum = (sizeof(table)/sizeof(*table));


for(i=0; i<size; i++)
{
if( strcmp(key, table[i]) == 0 )
{
*pos = i;
//break;
return rv;
}
}


//没有找到返回-1
if (i == size)
{
*pos = -1;
}
return rv;
}


#define DIM(a) (sizeof(a)/sizeof(*a))


int main61()
{
int inum = 0;
int pos = 0;
int a[10];
int i = 0;
//指针数组
char*   c_keyword[] = {
"while", 
"case",
"static",
"do"
};


searcheKeyTable( c_keyword, DIM(c_keyword),"do", &pos);
//searcheKeyTable( c_keyword, inum,"do", &pos);
printf("pos:%d\n", pos);
//searcheKeyTable(NULL, DIM(c_keyword),"do", &pos);
//printf("pos:%d\n", pos);
searcheKeyTable( c_keyword, DIM(c_keyword), "static", &pos);
printf("pos:%d\n", pos);


system("pause");
return ;
}


//main函数是操作系统调用的函数
//在程序执行的时候可以向main函数传递参数


/*
argc 命令行参数
argv 命令行参数数组
env  函数变量数组


int main();
int main(int argc);
int main(int argc, char *argv[])
*/




int main111(int argc, char* argv[], char**env)
{
int i = 0;
//main02_1();


printf("******************* Begin argv *******************\n");
for(i=0; i<argc; i++)
{
printf("%s\n", argv[i]);
}


// for(i=0; argv[i]!=NULL; i++)
// {
// printf("%s\n", argv[i]);
// }
printf("******************* End argv *******************\n");


printf("\n");
printf("\n");
printf("\n");


printf("******************* Begin env *******************\n");


for(i=0; env[i]!=NULL; i++)
{
printf("%s\n", env[i]);
}


printf("******************* End env*******************\n");


getchar();
}


int main()
{
int inum = 0;
int pos = 0;
int a[10];
int i = 0;
//指针数组
char*   c_keyword[] = {
"while", 
"case",
"static",
"do",
'\0'
};
// NULL   0  '\0'


for(i=0; c_keyword[i]!=NULL; i++)
{
printf("%s\n", c_keyword[i]);
}
system("pause");
}




13.野指针及其释放问题


#include "stdio.h"
#include "stdlib.h"
#include "string.h"


int myfree(char *p2)
{
if (p2 != NULL)//判断是否内存被操作系统占用
{
free(p2);//释放
p2 = NULL;//释放后最好“拴在”NULL处
}
}


void main()
{
//声明指针变量的时候null
char *p = NULL;//不初始化会产生野指针
p = (char *)malloc(100);//分配堆内存
//此处执行业务
myfree(p);//业务执行完毕释放内存
/*
if (p != NULL)
{
free(p);
p = NULL;
}
若重复释放内存也会产生野指针,导致程序崩溃   
*/
system("pause");
}



























1 字符串地址的测试

1.1 问题

测试字符串常量和字符数组 类型的变量地址是否相同。

1.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:字符串地址的测试

代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include <string.h>
  3. int main()
  4. {
  5. char *str = "This is a string.";
  6. char array[100];
  7. strcpy(array, str);
  8. printf("字符串常量的地址:%p\n", str);
  9. printf("字符数组的地址:%p\n", array);
  10. return 0;
  11. }

上述代码中,以下代码:


    
    
  1. char *str = "This is a string.";

定义了一个字符型指针str,用于指向一个字符串常量。

上述代码中,以下代码:


    
    
  1. printf("字符串常量的地址:%p\n", str);
  2. printf("字符数组的地址:%p\n", array);

使用printf函数分别输出字符串常量的地址和字符数组的地址。从输出结果可见,它们是不相同的。

1.3 完整代码

本案例的完整代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include <string.h>
  3. int main()
  4. {
  5. char *str = "This is a string.";
  6. char array[100];
  7. strcpy(array, str);
  8. printf("字符串常量的地址:%p\n", str);
  9. printf("字符数组的地址:%p\n", array);
  10. return 0;
  11. }

2 字符串的定义和使用

2.1 问题

定义一个int类型的指针,指向一个整型变量,然后分别使用&和*取得地址或者数据。

2.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:字符串的定义和使用

代码如下所示:


    
    
  1. #include <stdio.h>
  2. int main()
  3. {
  4. char str[5] = {};
  5. scanf("%4s", str);
  6. printf("%s\n", str);
  7. return 0;
  8. }

上述代码中,以下代码:


    
    
  1. char str[5] = {};

定义了一个字符数组str,该数组有5个元素。

上述代码中,以下代码:


    
    
  1. scanf("%4s", str);

使用scanf函数输入一个字符串。其中,%4s是限定输入的字符串中字符的个数不能大于4,否则将只输入前4个字符。指定值为4是因为字符数组str的长度为5,多出来的一个需要存储\0。

上述代码中,以下代码:


    
    
  1. printf("%s\n", str);

使用函数printf输出字符串。字符数组所对应的格式控制符是%s。

2.3 完整代码

本案例的完整代码如下所示:


    
    
  1. #include <stdio.h>
  2. int main()
  3. {
  4. char str[5] = {};
  5. scanf("%4s", str);
  6. printf("%s\n", str);
  7. return 0;
  8. }

3 字符串函数的使用

3.1 问题

使用指针实现字符串的函数strlen()、strcat()的功能,可以自定义两个函数。

3.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:字符串函数的使用

代码如下所示:


    
    
  1. #include <stdio.h>
  2. int mystrlen(char *str)
  3. {
  4. int count = 0;
  5. while (*str)
  6. {
  7. count++;
  8. str++;
  9. }
  10. return count;
  11. }
  12. char* mystrcat(char *s, char *d)
  13. {
  14. char* old = s;
  15. while(*s)
  16. s++;
  17. while(*d)
  18. {
  19. *s = *d;
  20. s++;
  21. d++;
  22. }
  23. return old;
  24. }
  25. int main()
  26. {
  27. char str[100] = "This is ";
  28. printf("str的长度为:%d\n", mystrlen(str));
  29. char* str1 = "a string";
  30. mystrcat(str, str1);
  31. printf("连接后的字符串为:%s\n", str);
  32. return 0;
  33. }

上述代码中,以下代码:


    
    
  1. int mystrlen(char *str)
  2. {
  3. int count = 0;
  4. while (*str)
  5. {
  6. count++;
  7. str++;
  8. }
  9. return count;
  10. }

定义了一个函数mystrlen,用于模拟库函数strlen的功能。该函数的参数为求长度的字符串。在该函数中,以下语句:


    
    
  1. int count = 0;

首先定义一个整型变量count,用于存储字符串中字符的个数。在该函数中,以下语句:


    
    
  1. while (*str)
  2. {
  3. count++;
  4. str++;
  5. }

设置一个循环,逐个计算字符串中字符的个数,当*str不为\0时,代表该字符串没有结束。在该函数中,以下语句:


    
    
  1. return count;

返回字符串的长度。

上述代码中,以下代码:


    
    
  1. char* mystrcat(char *s, char *d)
  2. {
  3. char* old = s;
  4. while(*s)
  5. s++;
  6. while(*d)
  7. {
  8. *s = *d;
  9. s++;
  10. d++;
  11. }
  12. return old;
  13. }

定义了一个函数mystrcat,用于模拟库函数strcat的功能。该函数的两个参数为将字符串d连接到字符串s的后面。在该函数中,以下语句:


    
    
  1. char* old = s;

首先保存被连接字符串的首地址,用于函数结束返回时作返回值用。在该函数中,以下语句:


    
    
  1. while(*s)
  2. s++;

设置一个循环找到被连接字符串的结束符\0,以便将另一个字符串连接到这个位置。在该函数中,以下语句:


    
    
  1. while(*d)
  2. {
  3. *s = *d;
  4. s++;
  5. d++;
  6. }

设置一个循环将字符串d的每一个字符拷贝到字符串s的结束字符\0开始的空间中,实现连接功能。在该函数中,以下语句:


    
    
  1. return old;

返回字符串s的首字符地址。

注意:由于上述循环中,指针s已经发生了变化,所以不能直接返回。

上述代码中,以下代码:


    
    
  1. int main()
  2. {
  3. char str[100] = "This is ";
  4. printf("str的长度为:%d\n", mystrlen(str));

首先,在主函数中定义一个字符数组,用于存储字符串"This is "。

然后,调用自定义函数mystrlen求得字符数组的长度,并输出。

上述代码中,以下代码:


    
    
  1. char* str1 = "a string";
  2. mystrcat(str, str1);
  3. printf("连接后的字符串为:%s\n", str);

首先,在主函数中定义一个字符指针,用于指向字符串"a string"。

然后,调用自定义函数mystrcat将字符指针指向的字符串拼接到字符数组str中,并输出。

最后,输出连接后的字符数组str。

3.3 完整代码

本案例的完整代码如下所示:


    
    
  1. #include <stdio.h>
  2. int mystrlen(char *str)
  3. {
  4. int count = 0;
  5. while (*str)
  6. {
  7. count++;
  8. str++;
  9. }
  10. return count;
  11. }
  12. char* mystrcat(char *s, char *d)
  13. {
  14. char* old = s;
  15. while(*s)
  16. s++;
  17. while(*d)
  18. {
  19. *s = *d;
  20. s++;
  21. d++;
  22. }
  23. return old;
  24. }
  25. int main()
  26. {
  27. char str[100] = "This is ";
  28. printf("str的长度为:%d\n", mystrlen(str));
  29. char* str1 = "a string";
  30. mystrcat(str, str1);
  31. printf("连接后的字符串为:%s\n", str);
  32. return 0;
  33. }

4 字符串函数的使用(续1)

4.1 问题

使用指针函数实现文件名和文件目录的拼接。

4.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:字符串函数的使用(续1)

代码如下所示:


    
    
  1. #include <stdio.h>
  2. char* filenamecat(char *path, char* name)
  3. {
  4. char* old = path;
  5. while(*path)
  6. path++;
  7. if (*(path - 1) != '/')
  8. {
  9. *path = '/';
  10. path++;
  11. }
  12. while(*name)
  13. {
  14. *path = *name;
  15. path++;
  16. name++;
  17. }
  18. return path;
  19. }
  20. int main()
  21. {
  22. char path[100] = "/home/tarena/";
  23. char* filename = "字符串函数的使用.c";
  24. filenamecat(path, filename);
  25. printf("带路径的文件名为:%s\n", path);
  26. return 0;
  27. }

上述代码中,下面代码行:


    
    
  1. char* filenamecat(char *path, char* name)
  2. {
  3. char* old = path;
  4. while(*path)
  5. path++;
  6. if (*(path - 1) != '/')
  7. {
  8. *path = '/';
  9. path++;
  10. }
  11. while(*name)
  12. {
  13. *path = *name;
  14. path++;
  15. name++;
  16. }
  17. return old;
  18. }

定义了一个函数filenamecat,用于拼接文件路径和文件名的功能。该函数的两个参数为将文件名字符串name连接到路径字符串path的后面。在该函数中,以下语句:


    
    
  1. char* old = path;

首先保存路径字符串的首地址,用于函数结束返回时作返回值用。在该函数中,以下语句:


    
    
  1. while(*path)
  2. path++;

设置一个循环找到路径字符串的结束符\0,以便将文件名字符串连接到这个位置。在该函数中,以下语句:


    
    
  1. if (*(path - 1) != '/')
  2. {
  3. *path = '/';
  4. path++;
  5. }

判断路径字符串的最后一个字符是否是/,如果不是则首先在路径字符串的最后添加字符/。在该函数中,以下语句:


    
    
  1. while(*name)
  2. {
  3. *path = *name;
  4. path++;
  5. name++;
  6. }

设置一个循环将文件名字符串name的每一个字符拷贝到路径字符串path的结束字符/后面的空间中,实现连接功能。在该函数中,以下语句:


    
    
  1. return old;

返回路径字符串path的首字符地址。

注意:由于上述循环中,指针path已经发生了变化,所以不能直接返回path。

上述代码中,下面代码行:


    
    
  1. int main()
  2. {
  3. char path[100] = "/home/tarena/";
  4. char* filename = "字符串函数的使用.c";

首先,定义一个数组path,用于保存路径名。

然后,定义一个指针filename,用于指向文件名。

上述代码中,下面代码行:


    
    
  1. filenamecat(path, filename);

调用自定义函数filenamecat,将文件名filename连接到路径名path的后面。

上述代码中,下面代码行:


    
    
  1. printf("带路径的文件名为:%s\n", path);

使用函数printf输出连接后的路径加文件名。

4.3 完整代码

本案例的完整代码如下所示:


    
    
  1. #include <stdio.h>
  2. char* filenamecat(char *path, char* name)
  3. {
  4. char* old = path;
  5. while(*path)
  6. path++;
  7. if (*(path - 1) != '/')
  8. {
  9. *path = '/';
  10. path++;
  11. }
  12. while(*name)
  13. {
  14. *path = *name;
  15. path++;
  16. name++;
  17. }
  18. return path;
  19. }
  20. int main()
  21. {
  22. char path[100] = "/home/tarena/";
  23. char* filename = "字符串函数的使用.c";
  24. filenamecat(path, filename);
  25. printf("带路径的文件名为:%s\n", path);
  26. return 0;
  27. }

5 字符串的基本操作

5.1 问题

测试不同类型的指针的算术运算。

5.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:字符串的基本操作

代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include <string.h>
  3. int main()
  4. {
  5. char str[100];
  6. strcpy(str, "tarena");
  7. printf("字符串为:%s\n", str);
  8. if (strcmp(str, "tarena"))
  9. printf("字符串str与tarena不同\n");
  10. else
  11. printf("字符串str与tarena相同\n");
  12. printf("sizeof(str) = %ld\n", sizeof(str));
  13. printf("strlen(str) = %lu\n", strlen(str));
  14. strcat(str, " C++方向");
  15. printf("字符串拼接后为:%s\n", str);
  16. printf("字符串拆分的前一部分为:%s\n", strtok(str, " "));
  17. printf("字符串拆分的后一部分为:%s\n", strtok(NULL, " "));
  18. char* p = strstr(str, "re");
  19. if (p)
  20. printf("子串re在字符串tarena中的位置为:%ld\n", p - str);
  21. else
  22. printf("子串re不在字符串tarena中\n");
  23. strcpy(str, "a 10 13.5");
  24. int i;
  25. double d;
  26. char c;
  27. sscanf(str, "%c%d%lf\n", &c, &i, &d);
  28. printf("%c %d %lf\n", c, i, d);
  29. memset(str, 0, sizeof(str));
  30. sprintf(str, "%c %d %lf\n", c, i, d);
  31. printf("%s\n", str);
  32. return 0;
  33. }

上述代码中,以下代码:


    
    
  1. char str[100];
  2. strcpy(str, "tarena");
  3. printf("字符串为:%s\n", str);

使用strcpy函数,将字符串"tarena"赋值到字符数组str中。

上述代码中,以下代码:


    
    
  1. if (strcmp(str, "tarena"))
  2. printf("字符串str与tarena不同\n");
  3. else
  4. printf("字符串str与tarena相同\n");

使用函数strcmp,逐个对比字符串"tarena"与字符数组str中的对应字符。如果所有对应字符均相同则返回0,否则返回非0值。

上述代码中,以下代码:


    
    
  1. printf("sizeof(str) = %ld\n", sizeof(str));
  2. printf("strlen(str) = %lu\n", strlen(str));

使用函数printf分别打印sizeof(str)和strlen(str)的值。从运行结果可以看出是不相同的。sizeof(str)输出的是字符数组str中所有元素所占的字节数。strlen(str)输出的是字符数组str中保存的字符串长度。

上述代码中,以下代码:


    
    
  1. strcat(str, " C++方向");
  2. printf("字符串拼接后为:%s\n", str);

使用函数strcat在字符串str的最后一个字符的后面连接上字符串" C++方向"。

上述代码中,以下代码:


    
    
  1. printf("字符串拆分的前一部分为:%s\n", strtok(str, " "));
  2. printf("字符串拆分的后一部分为:%s\n", strtok(NULL, " "));

使用函数strtok将刚拼接好的字符串str重新拆分成"tarena"和" C++方向"。

上述代码中,以下代码:


    
    
  1. char* p = strstr(str, "re");
  2. if (p)
  3. printf("子串re在字符串tarena中的位置为:%ld\n", p - str);
  4. else
  5. printf("子串re不在字符串tarena中\n");

首先,使用函数strstr在字符串str中查找子串"re"是否存在,如果存在则返回子串"re"的第一个字符r在字符串str的中的地址。然后,使用函数printf输出字符r在字符串中的位置。如果不存在,则输出子串不在字符串中。

上述代码中,以下代码:


    
    
  1. strcpy(str, "a 10 13.5");
  2. int i;
  3. double d;
  4. char c;
  5. sscanf(str, "%c%d%lf\n", &c, &i, &d);
  6. printf("%c %d %lf\n", c, i, d);

使用函数sscanf将字符串"a 10 13.5"中的字符a,整数10,双精度浮点数13.5转换到字符变量c,整型变量i,双精度浮点型变量d中。

上述代码中,以下代码:


    
    
  1. memset(str, 0, sizeof(str));
  2. sprintf(str, "%c %d %lf\n", c, i, d);
  3. printf("%s\n", str);

首先,将字符数组str中的所有元素清0。

然后,使用sprintf将字符变量c,整型变量i,双精度浮点型变量d中的值转换成字符串,存储到数组str中。

5.3 完整代码

本案例的完整代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include <string.h>
  3. int main()
  4. {
  5. char str[100];
  6. strcpy(str, "tarena");
  7. printf("字符串为:%s\n", str);
  8. if (strcmp(str, "tarena"))
  9. printf("字符串str与tarena不同\n");
  10. else
  11. printf("字符串str与tarena相同\n");
  12. printf("sizeof(str) = %ld\n", sizeof(str));
  13. printf("strlen(str) = %lu\n", strlen(str));
  14. strcat(str, " C++方向");
  15. printf("字符串拼接后为:%s\n", str);
  16. printf("字符串拆分的前一部分为:%s\n", strtok(str, " "));
  17. printf("字符串拆分的后一部分为:%s\n", strtok(NULL, " "));
  18. char* p = strstr(str, "re");
  19. if (p)
  20. printf("子串re在字符串tarena中的位置为:%ld\n", p - str);
  21. else
  22. printf("子串re不在字符串tarena中\n");
  23. strcpy(str, "a 10 13.5");
  24. int i;
  25. double d;
  26. char c;
  27. sscanf(str, "%c%d%lf\n", &c, &i, &d);
  28. printf("%c %d %lf\n", c, i, d);
  29. memset(str, 0, sizeof(str));
  30. sprintf(str, "%c %d %lf\n", c, i, d);
  31. printf("%s\n", str);
  32. return 0;
  33. }

6 字符串数组和命令行参数的使用

6.1 问题

定义三国五虎上将名单的数组,然后输入人名,判断是否是五虎上将。

6.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:定义五虎上将名单

使用字符指针数组作为名单。

代码如下:


    
    
  1. #include <stdio.h>
  2. int main(int argc, const char * argv[])
  3. {
  4. char *tiger[5] = {"GuanYu", "ZhangFei", "ZhaoYun", "MaChao", "HuangZhong"};
  5. return 0;
  6. }

步骤二:输入一个名字

定义一个字符数组,用于存储从控制台输入的名字。

代码如下:


    
    
  1. #include <stdio.h>
  2. int main(int argc, const char * argv[])
  3. {
  4. char *tiger[5] = {"GuanYu", "ZhangFei", "ZhaoYun", "MaChao", "HuangZhong"};
  5. char name[20];
  6. printf("请输入一个名字:");
  7. scanf("%s", name);
  8. return 0;
  9. }

步骤三:遍历数组

遍历数组,逐个将数组元素与输入的名字对比,查找是否为五虎上将之一。


    
    
  1. #include <stdio.h>
  2. #include <string.h>
  3. int main(int argc, const char * argv[])
  4. {
  5. char *tiger[5] = {"GuanYu", "ZhangFei", "ZhaoYun", "MaChao", "HuangZhong"};
  6. char name[20];
  7. printf("请输入一个名字:");
  8. scanf("%s", name);
  9. int i;
  10. for (i = 0; i < 5; i++)
  11. if (strcmp(name, tiger[i]) == 0)
  12. {
  13. printf("%s是五虎上将之一。\n", name);
  14. break;
  15. }
  16. if (i == 5)
  17. printf("%s不是五虎上将之一。\n", name);
  18. return 0;
  19. }

注意:strcmp函数需要包含string.h这个头函数。

6.3 完整代码

本案例的完整代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include <string.h>
  3. int main(int argc, const char * argv[])
  4. {
  5. char *tiger[5] = {"GuanYu", "ZhangFei", "ZhaoYun", "MaChao", "HuangZhong"};
  6. char name[20];
  7. printf("请输入一个名字:");
  8. scanf("%s", name);
  9. int i;
  10. for (i = 0; i < 5; i++)
  11. if (strcmp(name, tiger[i]) == 0)
  12. {
  13. printf("%s是五虎上将之一。\n", name);
  14. break;
  15. }
  16. if (i == 5)
  17. printf("%s不是五虎上将之一。\n", name);
  18. return 0;
  19. }

1 #include指令的使用

1.1 问题

测试#include的用法,包括gcc –I指定目录。

1.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:#include指令的使用

代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include "print.c"
  3. int main()
  4. {
  5. print();
  6. return 0;
  7. }

上述代码中,以下代码:


    
    
  1. #include "print.c"

使用#include 指令将文件print.c中的内容添加到本程序的文件中。

上述代码中,以下代码:


    
    
  1. int main()
  2. {
  3. print();
  4. return 0;
  5. }

在主程序中调用print.c中的函数print。

print.c代码如下所示:


    
    
  1. void print()
  2. {
  3. printf("调用在文件print.c中的print函数\n");
  4. }

上述代码中,以下代码:


    
    
  1. printf("调用在文件print.c中的print函数\n");

使用函数printf输出提示,该函数被调用了。

1.3 完整代码

本案例的完整代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include "print.c"
  3. int main()
  4. {
  5. print();
  6. return 0;
  7. }

print.c代码如下所示:


    
    
  1. void print()
  2. {
  3. printf("调用在文件print.c中的print函数\n");
  4. }

2 宏变量的使用

2.1 问题

测试宏变量的基本用法

2.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:宏变量的使用

代码如下所示:


    
    
  1. #include <stdio.h>
  2. #define SIZE 10
  3. #define BEGIN int main() {
  4. #define END return 0; }
  5. #define LOOP for (int i = 0; i < SIZE; i++)
  6. BEGIN
  7. int array[SIZE];
  8. printf("请输入10个整数(空格分隔):");
  9. LOOP scanf("%d", &array[i]);
  10. LOOP printf("array[%d] = %d\n", i, array[i]);
  11. END

上述代码中,以下代码:


    
    
  1. #define SIZE 10

定义一个宏变量SIZE,用于定义数组元素的个数。

上述代码中,以下代码:


    
    
  1. #define BEGIN int main() {

定义一个宏变量BEGIN,用于代替后面的主函数定义。

上述代码中,以下代码:


    
    
  1. #define END return 0; }

定义一个宏变量END,用于代替后面的主函数返回。

上述代码中,以下代码:


    
    
  1. #define LOOP for (int i = 0; i < SIZE; i++)

定义一个宏变量LOOP,用于代替设置一个循环。

上述代码中,以下代码:


    
    
  1. BEGIN

在预编译阶段,会被替代为以下语句:


    
    
  1. int main() {

上述代码中,以下代码:


    
    
  1. LOOP scanf("%d", &array[i]);

在预编译阶段,会被替代为以下语句:


    
    
  1. for (int i = 0; i < SIZE; i++) scanf("%d", &array[i]);

上述代码中,以下代码:


    
    
  1. LOOP printf("array[%d] = %d\n", i, array[i]);

在预编译阶段,会被替代为以下语句:


    
    
  1. for (int i = 0; i < SIZE; i++) printf("array[%d] = %d\n", i, array[i]);

上述代码中,以下代码:


    
    
  1. END

在预编译阶段,会被替代为以下语句:


    
    
  1. return 0; }

2.3 完整代码

本案例的完整代码如下所示:


    
    
  1. #include <stdio.h>
  2. #define SIZE 10
  3. #define BEGIN int main() {
  4. #define END return 0; }
  5. #define LOOP for (int i = 0; i < SIZE; i++)
  6. BEGIN
  7. int array[SIZE];
  8. printf("请输入10个整数(空格分隔):");
  9. LOOP scanf("%d", &array[i]);
  10. LOOP printf("array[%d] = %d\n", i, array[i]);
  11. END

3 宏函数的定义

3.1 问题

写一个宏函数,用它来验证一个日期是否合法。

3.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:宏函数的定义


    
    
  1. #define IS_DATE_VALID(year, month, day) \
  2. if ((year) > 2000 && (year) < 2020 && (month) > 0 && (month) < 13 && (day) > 0 && (day) < 32)\
  3. printf("日期合法\n");\
  4. else\
  5. printf("日期不合法\n");
  6. int main()
  7. {
  8. IS_DATE_VALID(2015,3,15);
  9. return 0;
  10. }

上述代码中,以下代码:


    
    
  1. #define IS_DATE_VALID(year, month, day) \
  2. if ((year) > 2000 && (year) < 2020 && (month) > 0 && (month) < 13 && (day) > 0 && (day) < 32)\
  3. printf("日期合法\n");\
  4. else\
  5. printf("日期不合法\n");

定义了一个宏函数IS_DATE_VALID,用于测试一个日期是否合法。上述代码中,\ 代表换行,当宏函数中的字符串比较长时,可以用它来作为换行符,不能用回车,因为回车代表宏函数定义结束。

上述代码中,以下代码:


    
    
  1. IS_DATE_VALID(2015,3,15);

在预编译阶段,会被替代为以下语句:


    
    
  1. if ((year) > 2000 && (year) < 2020 && (month) > 0 && (month) < 13 && (day) > 0 && (day) < 32)\
  2. printf("日期合法\n");\
  3. else\
  4. printf("日期不合法\n");

3.3 完整代码

本案例的完整代码如下所示:


    
    
  1. #define IS_DATE_VALID(year, month, day) \
  2. if ((year) > 2000 && (year) < 2020 && (month) > 0 && (month) < 13 && (day) > 0 && (day) < 32)\
  3. printf("日期合法\n");\
  4. else\
  5. printf("日期不合法\n");
  6. int main()
  7. {
  8. IS_DATE_VALID(2015,3,15);
  9. return 0;
  10. }

4 条件编译的使用

4.1 问题

写一个头文件,使用条件编译实现避免重复include。

4.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:条件编译的使用

main.c代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include "print.h"
  3. int main()
  4. {
  5. print();
  6. return 0;
  7. }

上述代码中,以下代码:


    
    
  1. #include "print.h"

使用#include 指令将文件print.h中的内容添加到本程序的文件中。

print.h文件,代码如下所示:


    
    
  1. #ifndef print_h
  2. #define print_h
  3. void print();
  4. #endif

上述代码中,以下代码:


    
    
  1. #ifndef print_h

使用条件编译指令判断宏名print_h是否已经定义,如果没有定义继续执行下面的语句,否则直接跳到#endif后面的语句执行。

上述代码中,以下代码:


    
    
  1. #define print_h

如果没有定义宏名print_h,则执行此语句定义该宏名,这样下一次再使用#ifndef判断宏名print_h是否已经定义时,由于已经定义将跳过#endif之间的语句,防止重复include。

print.c文件,代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include "print.h"
  3. void print()
  4. {
  5.     printf("避免重复include例题\n");
  6. }

上述代码中,以下代码:


    
    
  1. #include "print.h"

在main.c函数中已经有此语句,如果没有条件编译语句会多次包含print.h文件。

4.3 完整代码

本案例的完整代码如下所示:

main.c代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include "print.h"
  3. int main()
  4. {
  5. print();
  6. return 0;
  7. }

print.h文件,代码如下所示:


    
    
  1. #ifndef print_h
  2. #define print_h
  3. void print();
  4. #endif

print.c文件,代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include "print.h"
  3. void print()
  4. {
  5.     printf("避免重复include例题\n");
  6. }

5 头文件的使用

5.1 问题

写一组头文件,然后按照条件选择一个进行编译。

要求:实现避免重复导入并且测试static的作用。

5.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:short文件

short.h文件,代码如下所示:


    
    
  1. #ifndef short_h
  2. #define short_h
  3. short add(short a, short b);
  4. short sub(short a, short b);
  5. #endif

上述代码中,以下代码:


    
    
  1. #ifndef short_h
  2. #define short_h
  3. #endif

为防止重复include而预设的条件编译指令。

上述代码中,以下代码:


    
    
  1. short add(short a, short b);
  2. short sub(short a, short b);

声明短整型的加法add、减法sub运算函数。

short.c文件,代码如下所示:


    
    
  1. #include "short.h"
  2. short add(short a, short b)
  3. {
  4. return a + b;
  5. }
  6. short sub(short a, short b)
  7. {
  8. return a - b;
  9. }

上述代码中,以下代码:


    
    
  1. short add(short a, short b)
  2. {
  3. return a + b;
  4. }

短整型的加法add函数的定义。

上述代码中,以下代码:


    
    
  1. short sub(short a, short b)
  2. {
  3. return a - b;
  4. }

短整型的减法sub函数的定义。

步骤二:standard文件

standard.h文件,代码如下所示:


    
    
  1. #ifndef int_h
  2. #define int_h
  3. int add(int a, int b);
  4. int sub(int a, int b);
  5. #endif

上述代码中,以下代码:


    
    
  1. int add(int a, int b);
  2. int sub(int a, int b);

声明整型的加法add、减法sub运算函数。

standard.c文件,代码如下所示:


    
    
  1. #include "standard.h"
  2. int add(int a, int b)
  3. {
  4. return a + b;
  5. }
  6. int sub(int a, int b)
  7. {
  8. return a - b;
  9. }

整型的加法add、减法sub运算函数的定义。

步骤三:long文件

long.h文件,代码如下所示:


    
    
  1. #ifndef long_h
  2. #define long_h
  3. long add(long a, long b);
  4. long sub(long a, long b);
  5. #endif

上述代码中,以下代码:


    
    
  1. long add(long a, long b);
  2. long sub(long a, long b);

声明长整型的加法add、减法sub运算函数。

long.c文件,代码如下所示:


    
    
  1. #include "long.h"
  2. long add(long a, long b)
  3. {
  4. return a + b;
  5. }
  6. long sub(long a, long b)
  7. {
  8. return a - b;
  9. }

长整型的加法add、减法sub运算函数的定义。

步骤四:static文件

static.h文件,代码如下所示:


    
    
  1. #ifndef int_h
  2. #define int_h
  3. static void print();
  4. void call();
  5. #endif

上述代码中,以下代码:


    
    
  1. static void print();

声明了一个静态函数。该函数只能在本文件内部使用,在其他的.c文件中无法使用。

上述代码中,以下代码:


    
    
  1. void call();

定义了一个普通函数。

static.c文件,代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include "static.h"
  3. static int ex = 10;
  4. void print()
  5. {
  6. printf("这是静态函数,只能在本文件中使用\n");
  7. }
  8. void call()
  9. {
  10. print();
  11. printf("静态变量:%d\n", ex);
  12. }

上述代码中,以下代码:


    
    
  1. static int ex = 10;

定义了一个静态变量ex,该变量只能在本文件内部使用,在其他的.c文件中无法使用。

上述代码中,以下代码:


    
    
  1. void print()
  2. {
  3. printf("这是静态函数,只能在本文件中使用\n");
  4. }

静态函数print的定义。

上述代码中,以下代码:


    
    
  1. void call()
  2. {
  3. print();
  4. printf("静态变量:%d\n", ex);
  5. }

普通函数call的定义。由于静态函数print和静态变量ex,都与函数call在同一个文件中,所以以下语句:


    
    
  1. print();
  2. printf("静态变量:%d\n", ex);

对静态函数print的调用和静态变量ex的使用是合法的。

步骤五:main文件

main.h文件,代码如下所示:


    
    
  1. #ifndef main_h
  2. #define main_h
  3. #if defined(SHORT)
  4. #define FILE "short.h"
  5. #elif defined(STANDARD)
  6. #define FILE "standard.h"
  7. #elif defined(LONG)
  8. #define FILE "long.h"
  9. #endif
  10. #include FILE
  11. #endif

上述代码中,以下代码:


    
    
  1. #if defined(SHORT)
  2. #define FILE "short.h"

使用条件编译语句#if判断宏名SHORT是否定义,如果定义则定义宏名FILE为"short.h"。

上述代码中,以下代码:


    
    
  1. #elif defined(STANDARD)
  2. #define FILE "standard.h"

使用条件编译语句#elif判断宏名STANDARD是否定义,如果定义则定义宏名FILE为"standard.h"。

上述代码中,以下代码:


    
    
  1. #elif defined(LONG)
  2. #define FILE "long.h"

使用条件编译语句#elif判断宏名LONG是否定义,如果定义则定义宏名FILE为"long.h"。

上述代码中,以下代码:


    
    
  1. #include FILE

包含头文件FILE。

main.c文件,代码如下所示:


    
    
  1. #include <stdio.h>
  2. #define STANDARD
  3. #include "main.h"
  4. #include "static.h"
  5. extern int ex;
  6. int main()
  7. {
  8. printf("5 + 3 = %d\n", add(5, 3));
  9. printf("5 - 3 = %d\n", sub(5, 3));
  10. //print();
  11. //printf("静态变量:%d\n", ex);
  12. call();
  13. return 0;
  14. }

上述代码中,以下代码:


    
    
  1. #define STANDARD
  2. #include "main.h"

定义宏名STANDARD,这样在main.h中将执行#include "standard.h"。

上述代码中,以下代码:


    
    
  1. extern int ex;

声明一个外部变量ex。变量ex定义在static.c中,但由于变量ex被定义成静态变量,所以此语句没有意义。

上述代码中,以下代码:


    
    
  1. printf("5 + 3 = %d\n", add(5, 3));
  2. printf("5 - 3 = %d\n", sub(5, 3));

调用的函数add和函数sub是被定义在standard.c中的函数add和函数sub。

上述代码中,以下代码:


    
    
  1. //print();

调用函数print是非法的。因为函数print是静态函数,只能在static.c文件中被调用。

上述代码中,以下代码:


    
    
  1. //printf("静态变量:%d\n", ex);

使用变量ex是非法的。因为变量ex是静态变量,只能在static.c文件中被使用。

上述代码中,以下代码:


    
    
  1. call();

调用call函数是合法的,因为函数call不是静态函数。

5.3 完整代码

本案例的完整代码如下所示:

short.h文件,代码如下所示:


    
    
  1. #ifndef short_h
  2. #define short_h
  3. short add(short a, short b);
  4. short sub(short a, short b);
  5. #endif

short.c文件,代码如下所示:


    
    
  1. #include "short.h"
  2. short add(short a, short b)
  3. {
  4. return a + b;
  5. }
  6. short sub(short a, short b)
  7. {
  8. return a - b;
  9. }

standard.h文件,代码如下所示:


    
    
  1. #ifndef int_h
  2. #define int_h
  3. int add(int a, int b);
  4. int sub(int a, int b);
  5. #endif

standard.c文件,代码如下所示:


    
    
  1. #include "standard.h"
  2. int add(int a, int b)
  3. {
  4. return a + b;
  5. }
  6. int sub(int a, int b)
  7. {
  8. return a - b;
  9. }

long.h文件,代码如下所示:


    
    
  1. #ifndef long_h
  2. #define long_h
  3. long add(long a, long b);
  4. long sub(long a, long b);
  5. #endif

long.c文件,代码如下所示:


    
    
  1. #include "long.h"
  2. long add(long a, long b)
  3. {
  4. return a + b;
  5. }
  6. long sub(long a, long b)
  7. {
  8. return a - b;
  9. }

static.h文件,代码如下所示:


    
    
  1. #ifndef int_h
  2. #define int_h
  3. static void print();
  4. void call();
  5. #endif

static.c文件,代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include "static.h"
  3. static int ex = 10;
  4. void print()
  5. {
  6. printf("这是静态函数,只能在本文件中使用\n");
  7. }
  8. void call()
  9. {
  10. print();
  11. printf("静态变量:%d\n", ex);
  12. }

main.h文件,代码如下所示:


    
    
  1. #ifndef main_h
  2. #define main_h
  3. #if defined(SHORT)
  4. #define FILE "short.h"
  5. #elif defined(STANDARD)
  6. #define FILE "standard.h"
  7. #elif defined(LONG)
  8. #define FILE "long.h"
  9. #endif
  10. #include FILE
  11. #endif

main.c文件,代码如下所示:


    
    
  1. #include <stdio.h>
  2. #define STANDARD
  3. #include "main.h"
  4. #include "static.h"
  5. extern int ex;
  6. int main()
  7. {
  8. printf("5 + 3 = %d\n", add(5, 3));
  9. printf("5 - 3 = %d\n", sub(5, 3));
  10. //print();
  11. //printf("静态变量:%d\n", ex);
  12. call();
  13. return 0;
  14. }

6 makefile的使用

6.1 问题

分别使用静态初始化和动态初始化的方式对数组进行赋值,并循环打印数组元素。

6.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:input文件

input.h,代码如下所示:


    
    
  1. #ifndef input_h
  2. #define input_h
  3. void input();
  4. #endif

上述代码中,以下代码:


    
    
  1. void input();

声明了一个函数input。

input.c,代码如下所示:


    
    
  1. #include <stdio.h>
  2. extern int num;
  3. void input()
  4. {
  5. printf("请输入一个整数:");
  6. scanf("%d", &num);
  7. }

上述代码中,以下代码:


    
    
  1. extern int num;

声明了一个外部变量num。该变量定义在main.c中。

上述代码中,以下代码:


    
    
  1. void input()
  2. {
  3. printf("请输入一个整数:");
  4. scanf("%d", &num);
  5. }

定义了函数input。

步骤二:output文件

output.h,代码如下所示:


    
    
  1. #ifndef output_h
  2. #define output_h
  3. void output();
  4. #endif

上述代码中,以下代码:


    
    
  1. void output();

声明了一个函数output。

output.c,代码如下所示:


    
    
  1. #include <stdio.h>
  2. extern int num;
  3. void output()
  4. {
  5. printf("%d\n", num);
  6. }

上述代码中,以下代码:


    
    
  1. extern int num;

声明了一个外部变量num。该变量定义在main.c中。

上述代码中,以下代码:


    
    
  1. void output()
  2. {
  3. printf("%d\n", num);
  4. }

定义了函数output。

步骤三:main文件

代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include "input.h"
  3. #include "output.h"
  4. int num;
  5. int main()
  6. {
  7. input();
  8. output();
  9. return 0;
  10. }

上述代码中,以下代码:


    
    
  1. int num;

定义了一个全局变量num。

上述代码中,以下代码:


    
    
  1. input();
  2. output();

调用函数input,对变量num进行输入。

调用函数output,对变量num进行输出。

步骤四:makefile文件

代码如下所示:


    
    
  1. main:main.o input.o output.o
  2.      gcc main.c input.c output.c
  3. main.o:main.c
  4.      gcc main.c -c
  5. input.o:input.c
  6.      gcc input.c -c
  7. output.o:output.c
  8.      gcc output.c -c

可以使用makefile文件来一次性执行多条命令(批处理命令)。makefile带来的好处就是“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。

6.3 完整代码

本案例的完整代码如下所示:

input.h,代码如下所示:


    
    
  1. #ifndef input_h
  2. #define input_h
  3. void input();
  4. #endif

input.c,代码如下所示:


    
    
  1. #include <stdio.h>
  2. extern int num;
  3. void input()
  4. {
  5. printf("请输入一个整数:");
  6. scanf("%d", &num);
  7. }

output.h,代码如下所示:


    
    
  1. #ifndef output_h
  2. #define output_h
  3. void output();
  4. #endif

output.c,代码如下所示:


    
    
  1. #include <stdio.h>
  2. extern int num;
  3. void output()
  4. {
  5. printf("%d\n", num);
  6. }

main文件,代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include "input.h"
  3. #include "output.h"
  4. int num;
  5. int main()
  6. {
  7. input();
  8. output();
  9. return 0;
  10. }

makefile文件,代码如下所示:


    
    
  1. main:main.o input.o output.o
  2.      gcc main.c input.c output.c
  3. main.o:main.c
  4.      gcc main.c -c
  5. input.o:input.c
  6.      gcc input.c -c
  7. output.o:output.c
  8.      gcc output.c -c
Top



1 结构的声明和初始化

1.1 问题

定义测试结构体,并进行声明变量和初始化。

1.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:结构的声明和初始化

代码如下所示:


    
    
  1. #include <stdio.h>
  2. struct student
  3. {
  4. int id;
  5. char name[10];
  6. }stu1;
  7. int main()
  8. {
  9. stu1.id = 110;
  10. strcpy(stu1.name, "zhangsan");
  11. printf("学号:%d, 姓名:%s", stu1.id, stu1.name);
  12. struct student stu2 = {112, "lisi"};
  13. printf("学号:%d, 姓名:%s", stu2.id, stu2.name);
  14.      return 0;
  15. }

上述代码中,以下代码:


    
    
  1. struct student
  2. {
  3. int id;
  4. char name[10];
  5. }stu1;

定义学生结构体student。该结构体有两个成员,第一个成员是学号id,第二个成员是姓名name。在定义结构体student的同时定义了结构体变量stu1。

上述代码中,以下代码:


    
    
  1. stu1.id = 110;
  2. strcpy(stu1.name, "zhangsan");
  3. printf("学号:%d, 姓名:%s", stu1.id, stu1.name);

首先,将结构体变量stu1的学号成员赋值为110。

然后,将结构体变量stu1的姓名成员赋值为"zhangsan"。

最后,输出结构体变量stu1。

上述代码中,以下代码:


    
    
  1. struct student stu2 = {112, "lisi"};
  2. printf("学号:%d, 姓名:%s", stu2.id, stu2.name);

首先,定义了另一个结构体变量stu2,并对其进行初始化,将学号成员初始化为112,姓名成员初始化为"lisi"。

然后,输出结构体变量stu2。

1.3 完整代码

本案例的完整代码如下所示:


    
    
  1. #include <stdio.h>
  2. struct student
  3. {
  4. int id;
  5. char name[10];
  6. }stu1;
  7. int main()
  8. {
  9. stu1.id = 110;
  10. strcpy(stu1.name, "zhangsan");
  11. printf("学号:%d, 姓名:%s", stu1.id, stu1.name);
  12. struct student stu2 = {112, "lisi"};
  13. printf("学号:%d, 姓名:%s", stu2.id, stu2.name);
  14.      return 0;
  15. }

2 结构的声明和初始化(续1)

2.1 问题

定义结构点point,计算每个点离(0,0)的距离。

再定义结构rect(矩形),由两个点组成,计算矩形的面积。

2.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:结构的声明和初始化(续1)

代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <math.h>
  4. struct point
  5. {
  6. int x;
  7. int y;
  8. };
  9. struct rect
  10. {
  11. struct point left_top;
  12. struct point right_bottom;
  13. };
  14. int main()
  15. {
  16. struct point dot = {10, 20};
  17. int dis = sqrt(dot.x * dot.x + dot.y * dot.y);
  18. printf("点(%d,%d)到原点(0,0)的距离是:%d\n", dot.x, dot.y, dis);
  19. struct point a = {10, 10};
  20. struct point b = {50, 30};
  21. struct rect r = {a, b};
  22. int area = abs(r.left_top.x - r.right_bottom.x) * abs(r.left_top.y - r.right_bottom.y);
  23. printf("矩形的面积是:%d\n", area);
  24. return 0;
  25. }

上述代码中,以下代码:


    
    
  1. struct point
  2. {
  3. int x;
  4. int y;
  5. };

定义一个结构体point,该结构体有两个成员,x轴坐标x,y轴坐标y。

上述代码中,以下代码:


    
    
  1. struct rect
  2. {
  3. struct point left_top;
  4. struct point right_bottom;
  5. };

定义一个结构体rect,该结构体有两个成员,左上角点坐标left_top,右下角点坐标right_bottom。

上述代码中,以下代码:


    
    
  1. struct point dot = {10, 20};

定义一个结构体point的变量dot,并初始化为(10,20)。

上述代码中,以下代码:


    
    
  1. int dis = sqrt(dot.x * dot.x + dot.y * dot.y);

计算点dot到原点的距离。

上述代码中,以下代码:


    
    
  1. struct point a = {10, 10};
  2. struct point b = {50, 30};
  3. struct rect r = {a, b};

首先,定义两个结构体point的变量a和b,并初始化为(10,10)和(50,30)。

然后,定义结构体rect的变量r,并用上述两个结构体point的变量a和b初始化。

上述代码中,以下代码:


    
    
  1. int area = abs(r.left_top.x - r.right_bottom.x) * abs(r.left_top.y - r.right_bottom.y);

计算矩形r面积。

2.3 完整代码

本案例的完整代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <math.h>
  4. struct point
  5. {
  6. int x;
  7. int y;
  8. };
  9. struct rect
  10. {
  11. struct point left_top;
  12. struct point right_bottom;
  13. };
  14. int main()
  15. {
  16. struct point dot = {10, 20};
  17. int dis = sqrt(dot.x * dot.x + dot.y * dot.y);
  18. printf("点(%d,%d)到原点(0,0)的距离是:%d\n", dot.x, dot.y, dis);
  19. struct point a = {10, 10};
  20. struct point b = {50, 30};
  21. struct rect r = {a, b};
  22. int area = abs(r.left_top.x - r.right_bottom.x) * abs(r.left_top.y - r.right_bottom.y);
  23. printf("矩形的面积是:%d\n", area);
  24. return 0;
  25. }

3 结构的声明和初始化(续2)

3.1 问题

定义一个包含月份的名字和这个月天数的结构,用一个结构的数组保存一年中的所有月份和天数,在每个结构中保存一个月的名字和天数。

月份英文:January、February、March、April、May、June、July、August、September、October、November、December。

3.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:结构的声明和初始化(续2)

代码如下所示:


    
    
  1. #include <stdio.h>
  2. struct month
  3. {
  4. char name[10];
  5. int days;
  6. };
  7. int main()
  8. {
  9. struct month m[12] = {
  10. {"January", 31},
  11. {"February", 28},
  12. {"March", 31},
  13. {"April", 30},
  14. {"May", 31},
  15. {"June", 30},
  16. {"July", 31},
  17. {"August", 31},
  18. {"September", 30},
  19. {"October", 31},
  20. {"November", 30},
  21. {"December", 31}};
  22. for (int i = 0; i < 12; i++)
  23. printf("%d月的名字是:%s,天数是:%d\n", i + 1, m[i].name, m[i].days);
  24. return 0;
  25. }

上述代码中,以下代码:


    
    
  1. struct month
  2. {
  3. char name[10];
  4. int days;
  5. };

定义一个结构体month,该结构体有两个成员,该月的英文名字和该月的天数。

上述代码中,以下代码:


    
    
  1. struct month m[12] = {
  2. {"January", 31},
  3. {"February", 28},
  4. {"March", 31},
  5. {"April", 30},
  6. {"May", 31},
  7. {"June", 30},
  8. {"July", 31},
  9. {"August", 31},
  10. {"September", 30},
  11. {"October", 31},
  12. {"November", 30},
  13. {"December", 31}};

定义了一个结构体month的数组m,该数组有12个元素,每个元素代表一年中的一个月。定义数组m之后,对其进行了初始化。

注意:初始化时,每个数组元素的值用{}括起来。

3.3 完整代码

本案例的完整代码如下所示:


    
    
  1. #include <stdio.h>
  2. struct month
  3. {
  4. char name[10];
  5. int days;
  6. };
  7. int main()
  8. {
  9. struct month m[12] = {
  10. {"January", 31},
  11. {"February", 28},
  12. {"March", 31},
  13. {"April", 30},
  14. {"May", 31},
  15. {"June", 30},
  16. {"July", 31},
  17. {"August", 31},
  18. {"September", 30},
  19. {"October", 31},
  20. {"November", 30},
  21. {"December", 31}};
  22. for (int i = 0; i < 12; i++)
  23. printf("%d月的名字是:%s,天数是:%d\n", i + 1, m[i].name, m[i].days);
  24. return 0;
  25. }

4 结构做参数

4.1 问题

对比结构做参数的两种方式,使用->操作成员。

4.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:结构做参数

代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include <string.h>
  3. struct student
  4. {
  5. int id;
  6. char name[10];
  7. }stu;
  8. void print1(struct student stu)
  9. {
  10. printf("学号:%d, 姓名:%s\n", stu.id, stu.name);
  11. }
  12. void print2(struct student *p)
  13. {
  14. printf("学号:%d, 姓名:%s\n", p->id, p->name);
  15. }
  16. int main()
  17. {
  18. stu.id = 110;
  19. strcpy(stu.name, "zhangsan");
  20. print1(stu);
  21. print2(&stu);
  22. return 0;
  23. }

上述代码中,以下代码:


    
    
  1. struct student
  2. {
  3. int id;
  4. char name[10];
  5. }stu;

定义学生结构体student。该结构体有两个成员,第一个成员是学号id,第二个成员是姓名name。在定义结构体student的同时定义了结构体变量stu。

上述代码中,以下代码:


    
    
  1. void print1(struct student stu)
  2. {
  3. printf("学号:%d, 姓名:%s\n", stu.id, stu.name);
  4. }

定义了一个函数print1,用于打印结构体student的变量的值。该函数有一个参数,是结构体student的变量stu。这是值传递的方法。结构体student的变量stu将是实参的副本。

上述代码中,以下代码:


    
    
  1. void print2(struct student *p)
  2. {
  3. printf("学号:%d, 姓名:%s\n", p->id, p->name);
  4. }

定义了一个函数print2,用于打印结构体student的变量的值。该函数有一个参数,是结构体student的指针变量p。这是地址传递的方法。结构体student的指针变量p将指向实参。该函数中,p->id是用指针p指向的结构体student的变量的id成员。

上述代码中,以下代码:


    
    
  1. stu.id = 110;
  2. strcpy(stu.name, "zhangsan");

首先,将结构体student的变量stu的id成员赋值为110。

然后,将结构体student的变量stu的name成员赋值为"zhangsan"。

上述代码中,以下代码:


    
    
  1. print1(stu);

调用值传递方法的print1函数,实参为结构体student的变量stu。

上述代码中,以下代码:


    
    
  1. print2(&stu);

调用地址传递方法的print2函数,实参为结构体student的变量stu的地址。

4.3 完整代码

本案例的完整代码如下所示:


    
    
  1. #include <stdio.h>
  2. #include <string.h>
  3. struct student
  4. {
  5. int id;
  6. char name[10];
  7. }stu;
  8. void print1(struct student stu)
  9. {
  10. printf("学号:%d, 姓名:%s\n", stu.id, stu.name);
  11. }
  12. void print2(struct student *p)
  13. {
  14. printf("学号:%d, 姓名:%s\n", p->id, p->name);
  15. }
  16. int main()
  17. {
  18. stu.id = 110;
  19. strcpy(stu.name, "zhangsan");
  20. print1(stu);
  21. print2(&stu);
  22. return 0;
  23. }

5 结构做返回值

5.1 问题

测试直接返回结构的问题,并实现用结构指针参数带出数据。

5.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:结构做返回值

代码如下所示:


    
    
  1. #include <stdio.h>
  2. struct student
  3. {
  4. int id;
  5. char name[10];
  6. };
  7. struct student input1()
  8. {
  9. struct student stu;
  10. printf("输入学号:");
  11. scanf("%d", &stu.id);
  12. printf("输入姓名:");
  13. scanf("%s", stu.name);
  14. return stu;
  15. }
  16. void input2(struct student *p)
  17. {
  18. printf("输入学号:");
  19. scanf("%d", &p->id);
  20. printf("输入姓名:");
  21. scanf("%s", p->name);
  22. }
  23. int main()
  24. {
  25. struct student stu;
  26. stu = input1();
  27. printf("学号:%d, 姓名:%s\n", stu.id, stu.name);
  28. input2(&stu);
  29. printf("学号:%d, 姓名:%s\n", stu.id, stu.name);
  30. return 0;
  31. }

上述代码中,以下代码:


    
    
  1. struct student
  2. {
  3. int id;
  4. char name[10];
  5. };

定义学生结构体student。该结构体有两个成员,第一个成员是学号id,第二个成员是姓名name。

上述代码中,以下代码:


    
    
  1. struct student input1()
  2. {
  3. struct student stu;
  4. printf("输入学号:");
  5. scanf("%d", &stu.id);
  6. printf("输入姓名:");
  7. scanf("%s", stu.name);
  8. return stu;
  9. }

定义一个函数input1,用于输入。该函数中,以下语句:


    
    
  1. struct student stu;

定义了一个局部结构体student的变量stu。该函数中,以下语句:


    
    
  1. printf("输入学号:");
  2. scanf("%d", &stu.id);
  3. printf("输入姓名:");
  4. scanf("%s", stu.name);

输入学号和姓名。该函数中,以下语句:


    
    
  1. return stu;

返回结构体student的变量stu。

上述代码中,以下代码:


    
    
  1. void input2(struct student *p)
  2. {
  3. printf("输入学号:");
  4. scanf("%d", &p->id);
  5. printf("输入姓名:");
  6. scanf("%s", p->name);
  7. }

定义一个函数input2,用于输入,该函数有一个参数,是指向结构体student的指针变量p。该函数中,以下语句:


    
    
  1. printf("输入学号:");
  2. scanf("%d", &p->id);
  3. printf("输入姓名:");
  4. scanf("%s", p->name);

将学号和姓名输入到结构体student的指针变量p指向的实参。

上述代码中,以下代码:


    
    
  1. int main()
  2. {
  3. struct student stu;
  4. stu = input1();
  5. printf("学号:%d, 姓名:%s\n", stu.id, stu.name);

在主函数中,定义结构体student的变量stu,用于接收函数input1的返回值。

上述代码中,以下代码:


    
    
  1. input2(&stu);
  2. printf("学号:%d, 姓名:%s\n", stu.id, stu.name);

在主函数中,将结构体student的变量stu的地址作为实参传入input2,以输入其值。

5.3 完整代码

本案例的完整代码如下所示:


    
    
  1. #include <stdio.h>
  2. struct student
  3. {
  4. int id;
  5. char name[10];
  6. };
  7. struct student input1()
  8. {
  9. struct student stu;
  10. printf("输入学号:");
  11. scanf("%d", &stu.id);
  12. printf("输入姓名:");
  13. scanf("%s", stu.name);
  14. return stu;
  15. }
  16. void input2(struct student *p)
  17. {
  18. printf("输入学号:");
  19. scanf("%d", &p->id);
  20. printf("输入姓名:");
  21. scanf("%s", p->name);
  22. }
  23. int main()
  24. {
  25. struct student stu;
  26. stu = input1();
  27. printf("学号:%d, 姓名:%s\n", stu.id, stu.name);
  28. input2(&stu);
  29. printf("学号:%d, 姓名:%s\n", stu.id, stu.name);
  30. return 0;
  31. }

6 结构的对齐和补齐

6.1 问题

对齐是指由于内存分配会将结构中的变量分配到内存的边界上,以方便访问。每个成员放的位置是从本身长度的倍数位开始放。

补齐是指整个结构变量的长度要保持内部最长成员(超过4以4计)的倍数。如果不够,则补齐。

6.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:结构的对齐和补齐

代码如下所示:


    
    
  1. #include <stdio.h>
  2. struct size
  3. {
  4. char c;
  5. int i;
  6. };
  7. int main()
  8. {
  9. printf("sizeof(size) = %ld\n", sizeof(struct size));
  10. return 0;
  11. }

上述代码中,以下代码:


    
    
  1. struct size
  2. {
  3. char c;
  4. int i;
  5. };

定义一个结构体size,该结构体有两个成员,字符型变量c和整型变量i。

上述代码中,以下代码:


    
    
  1. printf("sizeof(size) = %ld\n", sizeof(struct size));

输出结构体size所占的字节数。从输出结果可知是8个字节。为什么不是5个字节呢?

原因是字符对齐的概念。当一个结构体被分配存储空间时,分配的方法是这样的:

首先,找到结构体中占字节数最多的成员,本案例中是整型变量i。

然后,以该成员所占的字节数为单位,为结构体的每个成员分配存储空间。在本案例中,为第一个成员字符型变量c分配4个字节,该成员用掉1个字节,剩下3个字节。字符型变量c所占的那个字节的编号设为0,剩下的3个字节编号依次为1、2、3。

最后,为第二个成员整型变量i分配存储空间,由于剩下的3个字节的编号依次为1、2、3,用这些编号对整型变量i所占字节数4求余,结果都不为0,所以剩下的3个字节都跳过。而再分配4个字节给整型变量i。

所以本案例的输出结果为8个字节,而不是5个字节。因为有三个字节没有用。

6.3 完整代码

本案例的完整代码如下所示:


    
    
  1. #include <stdio.h>
  2. struct size
  3. {
  4. char c;
  5. int i;
  6. };
  7. int main()
  8. {
  9. printf("sizeof(size) = %ld\n", sizeof(struct size));
  10. return 0;
  11. }

7 联合的基本用法

7.1 问题

联合就是一块内存对应不同的类型,并起了不同的名字,在使用时按照名字切换类型,而不是用类型转换。

7.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:联合的基本用法

代码如下所示:


    
    
  1. #include <stdio.h>
  2. union data
  3. {
  4. int a;
  5. int b;
  6. int c;
  7. };
  8. int main()
  9. {
  10. union data d;
  11. d.a = 10;
  12. printf("%d %d %d\n", d.a, d.b, d.c);
  13. d.b = 20;
  14. printf("%d %d %d\n", d.a, d.b, d.c);
  15. d.c = 30;
  16. printf("%d %d %d\n", d.a, d.b, d.c);
  17. return 0;
  18. }

上述代码中,以下代码:


    
    
  1. union data
  2. {
  3. int a;
  4. int b;
  5. int c;
  6. };

定义了一个联合data,该联合中有三个成员,这三个成员共用4个字节。

上述代码中,以下代码:


    
    
  1. union data d;
  2. d.a = 10;
  3. printf("%d %d %d\n", d.a, d.b, d.c);

首先,定义一个联合data的变量d。

然后,将联合data的变量d的第一个成员a赋值为10。

最后,使用printf输出联合data的三个成员的值。从输出结果可以看出,值都相同。而且,后两个成员未赋值。原因是联合data的三个成员共用4个字节。

上述代码中,以下代码:


    
    
  1. d.b = 20;
  2. printf("%d %d %d\n", d.a, d.b, d.c);

将联合data的变量d的第二个成员b赋值为20。从输出结果可以看出,联合data的三个成员的值都相同。原因是联合data的三个成员共用4个字节。

上述代码中,以下代码:


    
    
  1. d.c = 30;
  2. printf("%d %d %d\n", d.a, d.b, d.c);

将联合data的变量d的第三个成员c赋值为30。从输出结果可以看出,联合data的三个成员的值还是都相同。原因还是联合data的三个成员共用4个字节。

7.3 完整代码

本案例的完整代码如下所示:


    
    
  1. #include <stdio.h>
  2. union data
  3. {
  4. int a;
  5. int b;
  6. int c;
  7. };
  8. int main()
  9. {
  10. union data d;
  11. d.a = 10;
  12. printf("%d %d %d\n", d.a, d.b, d.c);
  13. d.b = 20;
  14. printf("%d %d %d\n", d.a, d.b, d.c);
  15. d.c = 30;
  16. printf("%d %d %d\n", d.a, d.b, d.c);
  17. return 0;
  18. }

8 枚举的基本用法

8.1 问题

枚举是一个整型常量的列表,一般来说,值是有限个。每个值都是枚举常量(整型)。可以定义时指定值,也可以多个枚举名表示同一个值。枚举常量可看成字面量。

8.2 步骤

实现此案例需要按照如下步骤进行。

步骤一:枚举的基本用法

代码如下所示:


    
    
  1. #include <stdio.h>
  2. enum WEEK
  3. {
  4. Monday = 1, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday,
  5. };
  6. int main()
  7. {
  8. int day;
  9. printf("请输入今天是星期几:");
  10. scanf("%d", &day);
  11. switch(day)
  12. {
  13. case Monday:
  14. printf("星期一\n");
  15. break;
  16. case Tuesday:
  17. printf("星期二\n");
  18. break;
  19. case Wednesday:
  20. printf("星期三\n");
  21. break;
  22. case Thursday:
  23. printf("星期四\n");
  24. break;
  25. case Friday:
  26. printf("星期五\n");
  27. break;
  28. case Saturday:
  29. printf("星期六\n");
  30. break;
  31. case Sunday:
  32. printf("星期日\n");
  33. break;
  34. }
  35. return 0;
  36. }

上述代码中,以下代码:


    
    
  1. enum WEEK
  2. {
  3. Monday = 1, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday,
  4. };

定义了一个枚举WEEK,是一个整型常量的列表,Monday是1,后面的值依次加1。

上述代码中,以下代码:


    
    
  1. int day;
  2. printf("请输入今天是星期几:");
  3. scanf("%d", &day);

首先,定义一个整型变量day,用于存储星期几的整数。

然后,使用printf提示输出输入星期几。

最后,使用scanf输入星期几到整型变量day中。

上述代码中,以下代码:


    
    
  1. switch(day)
  2. {
  3. case Monday:
  4. printf("星期一\n");
  5. break;
  6. case Tuesday:
  7. printf("星期二\n");
  8. break;
  9. case Wednesday:
  10. printf("星期三\n");
  11. break;
  12. case Thursday:
  13. printf("星期四\n");
  14. break;
  15. case Friday:
  16. printf("星期五\n");
  17. break;
  18. case Saturday:
  19. printf("星期六\n");
  20. break;
  21. case Sunday:
  22. printf("星期日\n");
  23. break;
  24. }

使用switch结构将整形星期几转换成字符串的星期几。其中每个case后面为一个枚举值。

8.3 完整代码

本案例的完整代码如下所示:


    
    
  1. #include <stdio.h>
  2. enum WEEK
  3. {
  4. Monday = 1, Tuesday, Wednesday, Thursday, Friday, Saturday, Sunday,
  5. };
  6. int main()
  7. {
  8. int day;
  9. printf("请输入今天是星期几:");
  10. scanf("%d", &day);
  11. switch(day)
  12. {
  13. case Monday:
  14. printf("星期一\n");
  15. break;
  16. case Tuesday:
  17. printf("星期二\n");
  18. break;
  19. case Wednesday:
  20. printf("星期三\n");
  21. break;
  22. case Thursday:
  23. printf("星期四\n");
  24. break;
  25. case Friday:
  26. printf("星期五\n");
  27. break;
  28. case Saturday:
  29. printf("星期六\n");
  30. break;
  31. case Sunday:
  32. printf("星期日\n");
  33. break;
  34. }
  35. return 0;
  36. }





  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值