问题1、数组和指针的区别
数组名不可以作为左值
char * p1 = "Hello World" ; //分配字符串常量,然后赋给 p1 ,一个指针型变量,是左值 char p2[ 20] = "Hello World" ; //分配一个数组,然后初始化为字符串,相当于一个常量,类型为数组,不是左值 *p1 = 'h' ; //p1可以指向别的地方,但hello world不能更改 p2[ 0] = 'h' ; //p2不能指向别的地方,但hello world可以更改
sizeof运算
sizeof(指针变量p1)是编译器分配给指针(也就是一个地址)的内存空间。
sizeof(数组指针常量p2)是整个数组占用空间的大小。但当数组作为函数参数进行传递时,数组就自动退化为同类型的指针。
取地址&运算
对数组名取地址&运算,得到的还是数组第一个元素的地址
对指针取地址&运算,得到的是指针所在的地址,也就是指向这个指针的指针。因此main函数的参数char *argv[],也可以写成char **argv。
参考
问题2、指针数组、数组指针与二维数组剖析
定义
指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身决定。它是“储存指针的数组”的简称。
数组指针:首先它是一个指针,它指向一个数组。在32 位系统下永远是占4 个字节,至于它指向的数组占多少字节,不知道。它是“指向数组的指针”的简称。
实例区分
int *p1[10]; //p1 是数组名,其包含10 个指向int 类型数据的指针,即指针数组 int (*p2)[10]; //p2 是一个指针,它指向一个包含10 个int 类型数据的数组,即数组指针 cout<<sizeof(a)<<" "<<sizeof(b); //4 40
实例分析
符号优先级: ()> [ ] > *
p1 先与“[]”结合,构成一个数组的定义,数组名为p1,int *修饰的是数组的内容,即数组的每个元素。
“*”号和p2 构成一个指针的定义,指针变量名为p2,int 修饰的是数组的内容,即数组的每个元素。数组在这里并没有名字,是个匿名数组。
数组和指针参数是如何被编译器修改的?
“数组名被改写成一个指针参数”规则并不是递归定义的。数组的数组会被改写成“数组的指针”,而不是“指针的指针”:
实参 所匹配的形参 数组的数组 char c[8][10]; char (*)[10]; 数组指针 指针数组 char *c[10]; char **c; 指针的指针 数组指针(行指针) char (*c)[10]; char (*c)[10]; 不改变 指针的指针 char **c; char **c; 不改变
void f(char **c){ cout<<c[1][2]<<endl; } int main(){ char *c[3]={"abc","def","ghi"}; f(c); }
*c[3] 表示这是一个一维数组,数组内的元素是char *类型,每个元素分别指向一个字符串。因为 [] 优先级大于 * 的优先级。
问题3、结构体和数组
数组中存放的是连续的元素,比如int、char等。同样,可以存放struct元素。
实例
struct UnionFieldInfo { int flag; //字段选中标识: 0 未选中 1 选中 char *field; // 选中字段 char *fieldDetail; //选中字段在数据库中具体表示 }; struct UnionFieldInfo HACAlarmAudioInfo[] = { {0, "time", "events.time"}, {0, "matchRule", "events.content"}, {0, "level", "events.rule_level"}, {0, "userName", "session.username"}, {-1, NULL, NULL}, }; /* int flag*/ int UnionGetAlarmFieldInit() { int i = 0; for (i = 0; i < sizeof(HACAlarmAudioInfo)/sizeof(HACAlarmAudioInfo[0]); i++) { HACAlarmAudioInfo[i].flag = 0; //init } return 0; } /* get field */ static char * UnionGetAlarmField(char *Field) { int i = 0; for (i = 0; i < sizeof(HACAlarmAudioInfo)/sizeof(HACAlarmAudioInfo[0]); i++) { if(HACAlarmAudioInfo[i].flag == 1) { return HACAlarmAudioInfo[i].field; } } return NULL; } /* make fielddetail sql */ int UnionMakeAlarmFieldSQL(char *sql) { int i = 0; for (i = 0; i < sizeof(HACAlarmAudioInfo)/sizeof(HACAlarmAudioInfo[0]); i++) { if(HACAlarmAudioInfo[i].flag == 1) { sprintf(sql+strlen(sql), "%s, ", HACAlarmAudioInfo[i].fieldDetail); } } /*cancel ", "*/ if(strlen(sql) >= strlen(", ")) { tinysql[strlen(sql)-strlen(", ")] = 0; } }
一维数组HACAlarmAudioInfo中存放的是struct UnionFieldInfo元素。
问题4、带逗号的字符串分隔
使用strtok进行分隔带有逗号的字符串
if (strlen(stApplyInfo.userGroupID)) { char *tmpstr = NULL; char s[2] = ","; tmpstr = strtok(stApplyInfo.userGroupID, s); //返回被分解的第一个子字符串,如果没有可检索的字符串,则返回一个空指针。 while(tmpstr) { memset(sql, 0, 2048); UnionDBPrintf(sql, 2048, "insert into workOrder_userGroup(workOrder, userGroupID) values('%s','%d')", worknote, atoi(tmpstr)); UnionLogDebugEx("draw.log","sql=[%s]",sql); UnionErr = UnionDBInsert(sql, &row, &errmsg); if (UnionErr) { UnionLogErrEx("draw.log", "db insert failed:[%s]", errmsg); free(sql); return UnionErr; } tmpstr = strtok(NULL, s); } }
问题5、字符串拷贝函数
最好使用memcpy()、snprintf()函数进行字符串拷贝。而strcpy()、strncpy()不太好。
strcpy()容易溢出,只用于字符串的拷贝。
strcpy(p+1, p); //内存重叠
char * strcpy(char * dest, const char * src) // 实现src到dest的复制 { if ((src == NULL) || (dest == NULL)) //判断参数src和dest的有效性 { return NULL; } char *strdest = dest; //保存目标字符串的首地址 while ((*strDest++ = *strSrc++)!='