问题1、数组和指针的区别

数组名不可以作为左值

char * p1 = "Hello World" ;       //分配字符串常量,然后赋给 p1  ,一个指针型变量,是左值
char p2[ ] = "Hello World" ; //分配一个数组,然后初始化为字符串,相当于一个常量,类型为数组,不是左值 *p1 = 'h' ; //p1可以指向别的地方,但hello world不能更改
p2[ ] = 'h' ; //p2不能指向别的地方,但hello world可以更改 

sizeof运算

sizeof(指针变量p1)是编译器分配给指针(也就是一个地址)的内存空间。
sizeof(数组指针常量p2)是整个数组占用空间的大小。但当数组作为函数参数进行传递时,数组就自动退化为同类型的指针。

取地址&运算

对数组名取地址&运算,得到的还是数组第一个元素的地址
对指针取地址&运算,得到的是指针所在的地址,也就是指向这个指针的指针。因此main函数的参数char *argv[],也可以写成char **argv。

参考

二维数组名和二级指针

问题2、指针数组、数组指针与二维数组剖析

定义

指针数组:首先它是一个数组,数组的元素都是指针,数组占多少个字节由数组本身决定。它是“储存指针的数组”的简称。 

数组指针:首先它是一个指针,它指向一个数组。在32 位系统下永远是占4 个字节,至于它指向的数组占多少字节,不知道。它是“指向数组的指针”的简称。

实例区分

int *p1[];  //p1 是数组名,其包含10 个指向int  类型数据的指针,即指针数组

int (*p2)[];  //p2 是一个指针,它指向一个包含10 个int  类型数据的数组,即数组指针

cout<<sizeof(a)<<" "<<sizeof(b);   //4 40

实例分析

符号优先级: ()> [ ] > *

p1 先与“[]”结合,构成一个数组的定义,数组名为p1,int *修饰的是数组的内容,即数组的每个元素。

“*”号和p2 构成一个指针的定义,指针变量名为p2,int 修饰的是数组的内容,即数组的每个元素。数组在这里并没有名字,是个匿名数组。

数组和指针参数是如何被编译器修改的?

“数组名被改写成一个指针参数”规则并不是递归定义的。数组的数组会被改写成“数组的指针”,而不是“指针的指针”:

实参                                                      所匹配的形参

数组的数组             char c[][];                  char (*)[];           数组指针

指针数组               char *c[];                    char **c;               指针的指针

数组指针(行指针)      char (*c)[];                  char (*c)[];          不改变

指针的指针             char **c;                       char **c;               不改变
void f(char **c){
cout<<c[][]<<endl;
} int main(){
char *c[]={"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[] = ","; tmpstr = strtok(stApplyInfo.userGroupID, s); //返回被分解的第一个子字符串,如果没有可检索的字符串,则返回一个空指针。
while(tmpstr)
{
memset(sql, , );
UnionDBPrintf(sql, , "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++)!='\0'); //把src字符串的内容复制到dest下   return strdest;
}

strncpy()标准用法

strncpy(path, src, sizeof(path) - );
path[sizeof(path) - ] = '/0';
len = strlen(path);

memcpy将N个字节的源内存地址的内容拷贝到目标内存地址中。

对于需要复制的内容没有限制,复制任意内容,例如字符数组、整型、结构体、类等。

当源内存和目标内存存在重叠时,memcpy会出现错误。

memcpy(p+1, p, 512); //内存重叠

void* memcpy(void* dest, const void* src, size_t n)
{
assert((NULL != dest) && (NULL != src));
char* d = (char*) dest;
const char* s = (const char*) src; while(n-–)
*d++ = *s++;
return dest;
}

内存重叠

函数strcpy和函数memcpy都没有对内存重叠做处理的,使用这两个函数的时候只有程序员自己保证源地址和目标地址不重叠,或者使用memmove函数进行内存拷贝。

memmove函数对内存重叠做了处理。内存重叠时,使用strcpy函数则程序会崩溃。使用memcpy的话程序会等到错误的结果。

memmove将N个字节的源内存地址的内容拷贝到目标内存地址中

当源内存和目标内存存在重叠时,能正确地实施拷贝,但这也增加了一点点开销。

void* memmove(void* dest, const void* src, size_t n)
{
assert((NULL != dest) && (NULL != src));
char* d = (char*) dest;
const char* s = (const char*) src; if (s>d)
{
// start at beginning of s
while (n--)
*d++ = *s++;
}
else if (s<d)
{
// start at end of s
d = d+n-;
s = s+n-; while (n--)
*d-- = *s--;
}
return dest; //do nothing
}
()内存低端 <-----s-----> <-----d-----> 内存高端 start at end of s
()内存低端 <-----s--<==>--d-----> 内存高端 start at end of s
()内存低端 <-----sd-----> 内存高端 do nothing
()内存低端 <-----d--<==>--s-----> 内存高端 start at beginning of s
()内存低端 <-----d-----> <-----s-----> 内存高端 start at beginning of s

(1)当源内存的首地址等于目标内存的首地址时,不进行任何拷贝

(2)当源内存的首地址大于目标内存的首地址时,实行正向拷贝

(3)当源内存的首地址小于目标内存的首地址时,实行反向拷贝

问题6、字符串查找函数

strstr() 函数

char *strstr(const char *haystack, const char *needle)

参数说明:
haystack -- 要被检索的 C 字符串。
needle -- 在 haystack 字符串内要搜索的小字符串。 返回值:
该函数返回在 haystack 中第一次出现 needle 字符串的位置,如果未找到则返回 null。 实例:
ret = strstr("RUNOOB", "NOOB");
printf("子字符串是: %s\n", ret); //"NOOB"

代码实现

#include <cstring>
#include <iostream>
#include <cassert> using namespace std; char* my_strstr(char* str, char* sub)
{
assert(str != NULL);
assert(sub != NULL); int str_len = strlen(str);
int sub_len = strlen(sub); if (str_len < sub_len) /*不用比较,肯定不是*/
{
return NULL;
} if (str_len != && sub_len == ) /*aaaaaaaaaaaaaaaaaa, "" ,比较需要花费时间很多*/
{
cout << "子串为空。。。" << endl; return NULL;
} if (str_len == && sub_len == ) /*都为空可以直接返回*/
{
cout << "原串和子串都为空 !" << endl; return str;
} for (int i = ; i != strlen(str); ++i)
{
int m = , n = i; cout << "原串剩余的长度 : " << strlen(str + i) << endl;
cout << "子串的长度 : " << sub_len << endl; if (strlen(str + i) < sub_len) /*往后找如果原串长度不够了,则肯定不是*/
{
cout << "子串太长啦。。。" << endl; return NULL;
} if (str[n] == sub[m])
{
while (str[n++] == sub[m++])
{
if (sub[m] == '\0')
{
return str + i;
}
}
}
} return NULL;
}

问题7、const用法

const与指针

const放在*左边表示指针指向的是一个常量,放在*后面表示这是一个常量指针。

int a=, b=;

const int* p = &a;  //p指向的是一个常量,即p的内容为常量
int const* p = &a; //p指向的是一个常量,即p的内容为常量
p = &b; //可以改变p的指向
//*p = 20; //错误:但是不能改变a的内容 int* const p1 = &a; //p1是一个常量指针,表明指针p1是常量不能进行修改,但是p指的内容是可以修改的
*p1 = ; //p指的内容是可以修改的
//p1=&b; //错误:指针p的指向不能修改

const修饰函数参数

const修改函数参数表示参数不可修改,可起到保护的作用。按值传递的参数加上const是没意义的,因为参数传递的时候是拷贝的。按引用传递时加上const保证传出参数的同时对象也不会被更改。

void foo(const int a)
{
// a=10; //错误,函数参数不可改变
}

const修改成员函数

const修改成员表示该函数不能修改任何成员变量的值,同时不能调用任何非const修饰的函数,因为没有const修饰的函数会修改成员变量的值。const修饰函数放在最后面。

#include<iostream>

using namespace std;

const int a()//返回值为const
{
return ;
} class A
{
public: int temp;
int fun (int x)const//类中成员函数的函数体由const修饰时,不能改变成员变量的值
{
//temp=5;//修改了temp的值,出错
return ;
}
}; int main()
{
//int v=a();//a()只能作为右值,可以赋给int v和const int v
const int v=a();
cout<<v;
A a;
a.fun();
return ;
}

const修饰返回值

一、常量可以赋给常量和变量。

二、指向常量的指针必须必须赋给指向常量的指针,指向变量的指针可以赋给指向变量或常量的指针。

const char* foo()
{
const char a = 'c';
const char* p = &a;
return p;
} char* const foo2()
{
char a = 'c';
char* const p = &a;
return p;
} const char* const foo3()
{
const char a = 'c';
const char* const p = &a;
return p;
} int main()
{
//char* p = foo(); //error,指向常量的指针不能赋给指向变量的指针
const char* p = foo(); //指向常量的指针
const char* const p4 = foo(); //把变量指针赋给常量指针,合法 char* p2 = foo2(); //把常量指针赋给变量指针,合法
char* const p3 = foo2(); //常量指针
const char* p5 = foo2(); //指向变量的指针可以赋给指向常量的指针 const char* const p6 = foo3(); //指向常量的常量指针
const char* p7 = foo3(); //指向常量的变量指针
//char* const p8 = foo3(); //指向变量的常量指针,error
//char* p9 = foo3(); //指向变量的变量指针,error cin.get();
return ;
}

问题8、代理中的echo调试记录

各种代理(ssh等)调试日志,很难获取。可以使用echo命令,通过以下简单几行代码进行调试。

int ret = ;
char log_cmd[] = {}, buff[] = {};
snprintf(buff, , "%s", "test log by system()");
snprintf(log_cmd, , "echo '%s' >> /tmp/log.txt", buff);
ret = system(log_cmd);

问题9、非root帐户下密钥认证脚本生成中的用户主目录获取

Linux 命令行下可通过 echo $HOME进行获取。

//获取当前用户主目录
char *UnionGetRealHomeDir()
{
char *path = getenv("HOME");
return path;
}

参考

C/C++面试常见的几个库函数详解

内存管理基础

获取文件最近修改时间戳

Linux 时间戳和标准时间转换

C/C++常见问题汇总的更多相关文章

  1. CentOS安装Oracle数据库详细介绍及常见问题汇总

    一.安装前准备 1.软件硬件要求 操作系统:CentOS 6.4(32bit)Oracle数据库版本:Oracle 10g(10201_database_linux32.zip)最小内存:1G(检查命 ...

  2. SVN集中式版本控制器的安装、使用与常见问题汇总

    SVN是Subversion的简称,是一个开放源代码的版本控制系统,它采用了分支管理系统,集中式版本控制器 官方网站:https://www.visualsvn.com/ 下载右边的服务器端,左边的客 ...

  3. H5项目常见问题汇总及解决方案

    H5项目常见问题汇总及解决方案 H5   2015-12-06 10:15:33 发布 您的评价:       4.5   收藏     4收藏 H5项目常见问题及注意事项 Meta基础知识: H5页 ...

  4. Installshield脚本拷贝文件常见问题汇总

    原文:Installshield脚本拷贝文件常见问题汇总 很多朋友经常来问:为什么我用CopyFile/XCopyFile函数拷贝文件无效?引起这种情况的原因有很多,今天略微总结了一下,欢迎各位朋友跟 ...

  5. MVC 网站部署常见问题汇总

    一:TGIShare项目是一个MVC5的网站程序,部署在了IIS上,使用的Windows验证方式,并在本机设置了计划任务定时调用某个地址执行命令.问题汇总如下: 1.Window Server 200 ...

  6. J2EE进阶(十)SSH框架整合常见问题汇总(一)

    SSH框架整合常见问题汇总(一) 前言 以下所列问题具有针对性,但是遇到同类型问题时均可按照此思路进行解决. HTTP Status 404 - No result defined for actio ...

  7. mysql进阶(十六)常见问题汇总

    mysql进阶(十六)常见问题汇总 MySQL视图学习: http://www.itokit.com/2011/0908/67848.html 执行删除操作时,出现如下错误提示: 出现以上问题的原因是 ...

  8. 转---CentOS安装Oracle数据库详细介绍及常见问题汇总

    一.安装前准备 1.软件硬件要求 操作系统:CentOS 6.4(32bit)Oracle数据库版本:Oracle 10g(10201_database_linux32.zip)最小内存:1G(检查命 ...

  9. (转)CloudStack 安装及使用过程中常见问题汇总

    CloudStack 安装及使用过程中常见问题汇总             在做工程项目中对CloudStack 安装及使用过程中常见的几个问题及如何解决做一个总结.   1.Windows XP虚拟 ...

  10. thymeleaf的常见问题汇总

    thymeleaf的常见问题汇总 1.thymeleaf th:href 多个参数传递格式 th:href="@{/Controller/update(param1=1,param2=${p ...

随机推荐

  1. sql server 备份语句

    1.BACKUP DATABASE your_database TO DISK = 'diff.bak'with DIFFERENTIAL #差异备份,仅备份数据2.BACKUP DATABASE y ...

  2. eNSP路由器输出 '#' 无法启动的一种解决方法

    试过网上的改virtulboxIP.关防火墙.改兼容性等方法,都不成功.后来发现通过改变选择路由器的型号可以启动. 如果对路由器没有特殊需求,可以选择型号数字较小的试一下(最低可以选择Router).

  3. 【Abode Air程序开发】打包并导出

    打包并导出 将移动设备应用程序打包并导出到在线商店 导出用于发行的 Android APK 包 导出用于发行的 Apple iOS 包 使用命令行进行创建.测试和部署 使用 mxmlc 编译手机应用程 ...

  4. Sql 备忘——行号

    SELECT row_number() over(order by Product.ID) as [row_number]

  5. Angular中ngx-image-cropper图片裁剪的使用

    GitHub示例源码地址:https://github.com/luoruiemail/ngx-image-cropper 下载下来之后,执行yarn install安装相关node_modules包 ...

  6. teach-es6-1

    ------------------------------------------------------------------------字符串连接: 之前: var str=''; var s ...

  7. ARST第三周打卡

    Algorithm : 做一个 leetcode 的算法题 //二位数组查找 题目描述 //在一个二维数组中(每个一维数组的长度相同),每一行都按照从左到右递增的顺序排序,每一列都按照从上到下递增的顺 ...

  8. Coloring Edges(有向图环染色)-- Educational Codeforces Round 72 (Rated for Div. 2)

    题意:https://codeforc.es/contest/1217/problem/D 给你一个有向图,要求一个循环里不能有相同颜色的边,问你最小要几种颜色染色,怎么染色? 思路: 如果没有环,那 ...

  9. 用python实现自己的http服务器——多进程、多线程、协程、单进程非堵塞版、epoll版

    了解http协议 http请求头 GET / HTTP/1.1 Host: www.baidu.com Connection: keep-alive Pragma: no-cache Cache-Co ...

  10. Ruby Rails学习中:调试信息和 Rails 的三种环境,Users 资源,调试器,Gravatar 头像和侧边栏

    注册 一.调试信息和 Rails 环境 现在咱们要实现的用户资料页面是我们这个应用中第一个真正意义上的动态页面.虽然视图的代码不会动态改变, 不过每个用户资料页面显示的内容却是从数据库中读取的.添加动 ...