五年前的随笔(三)

  1. mysql导出表结构
    使用mysqldump命令

格式
mysqldump YourDatabaseName –user=YourUserName –password=YourPassword

YourDatabaseName是你想处理的数据库名
YourUserName和YourPassword 对应你的授权口令

如果只需要导出表的结构,那么可以使用mysqldump的 -d 选项

导出整个库的表结构如下:
mysqldump -uroot -p -d databasename > createtab.sql,
如果只想导出 表 test1,test2,test3 的 表结构 和 数据呢?
该如何导出?
mysqldump -uroot -p -d databasename test1 test2 test3 > createtab.sql
– 上面的是导出指定表结构,下面这个可以导出指定表结构和数据
mysqldump -uroot -p –tables databasename > createtab.sql
mysqldump -uroot -p -d databasename test1 test2 test3 > createtab.sql

run–>cmd—>
c:mysqldump -uroot -pjava csincity>20090115.sql回车即可,后面不要加分号,此时导出的为数据库中所有的数据。

c:mysqldump -d -uroot -pjava csincity>20090115.sql
导出的为数据库中所有表的结构

c:mysqldump -uroot -pjava csincity user>20090115.sql
导出的为数据库中user表的结构

2. HTTP协议问题和线程池
如果发送请求特别多,返回来的数据是存在缓存中么,可不可能因为缓存大小有限,收不到发送出去的请求数据呢。
linux的线程池,用c++实现

3. MD5 SHA
MD5用的是哈希函数,在计算机网络中应用较多的不可逆加密算法(不可逆加密算法的特征是加密过程中不需要使用密钥,输入明文后由系统直接经过加密算法处理成密文,这种加密后的数据是无法被解密的,只有重新输入明文,并再次经过同样不可逆的加密算法处理,得到相同的加密密文并被系统重新识别后,才能真正解密。)有RSA公司发明的MD5(消息摘要)算法和由美国国家技术标准研究所建议的安全散列算法SHA。

4. 在C++的类中,普通成员函数不能作为pthread_create的线程函数
在C++的类中,普通成员函数不能作为pthread_create的线程函数,如果要作为pthread_create中的线程函数,必须是static !
在C语言中,我们使用pthread_create创建线程,线程函数是一个全局函数,所以在C++中,创建线程时,也应该使用一个全局函数。static定义的类的成员函数就是一个全局函数。
例如:

//thread.h
#include <pthread.h>
class Thread
{
private:
pthread_t pid;
private:
static void * start_thread(void *arg); //静态成员函数
public: 
int start();
virtual void run() = 0;//基类中的虚函数要么实现,要么是纯虚函数(绝对不允许声明不实现,也不纯虚)
};
int Thread::start()
{
if(pthread_create(&pid,NULL,start_thread,(void *)this) != 0) //创建一个线程(必须是全局函数)
{ 
return -1;
} 
return 0;
}
void* Thread::start_thread(void *arg) //静态成员函数只能访问静态变量或静态函数,通过传递this指针进行调用
{
Thread *ptr = (Thread *)arg;
ptr->run(); //线程的实体是run
}
//thread.cpp
#include <unistd.h>
#include <stdio.h>
#include "thread.h" 
#include <stdlib.h>
class MyThread:public Thread
{
public: 
void run();
};
void MyThread::run()
{
printf("hello world\n");
}

int main(int argc,char *argv[])
{
MyThread test;
test.run();
sleep(1);
return 0;
}



6. 不能够打开’config.xml’文件
linux下对open files 的数目有限制,一般为1024,如果程序要循环运行,运行一次打开一次的话,可能最终达到数目上限,不能打开文件。
解决方法用system方法,系统调用后会自动释放系统资源,循环的系统调用。

7. 在 Linux 上实现基于 Socket 的多进程实时通信
套接口(Socket)为目前Linux上最为广泛使用的一种的进程间通信机制,与其他的Linux通信机制不同之处在于除了它可用于单机内的进程间通信以外,还可用于不同机器之间的进程间通信。但是由于Socket本身不支持同时等待和超时处理,所以它不能直接用来多进程之间的相互实时通信。
本文提出一个基于Socket的多进程之间通信的实现方法。原理是建立一个进程专门用来做为通信服务器(server)来中转各个进程之间的通信。它首先启动一个用来监视连接要求的listening Socket,并把它的描述(Descriptor)号加入到一个事先定义好的fd_set的集合中,这个fd_set的集合用来存放listening Socket和后来生成的通信Socket的描述号。Server运用system call select来实时检查是否有数据到达这个集合中的任何一个socket,如果有数据到达listening Socket,则这一定是客户端发起的连接请求,于是生成一个新的通信Socket与该客户端连接,将生成的Socket描述号加入到fd_set的集合中,将客户端的ID号和与之对应的Socket的描述号记录在ID登记表中。如果有数据到达某个通信Socket,则这一定是某个客户端发起的通信请求,读出数据并取出收信客户端ID号,在ID登记表中找到与之对应的Socket描述号,将数据通过对应Socket传送到收信客户端。
其他各进程作为客户端(client)。客户端的动作是首先建立通信Socket连接服务器端,然后通过通信Socket进行送信和收信。
下面给出具体的程序实现和说明,
首先给出Server端的程序,在这里假设有两个客户端要进行实时通信,ClientA向ClientB发送字符1,ClientB向ClientA发送字符2。

  #include  <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/un.h>
#include <sys/time.h>
#include <sys/ioctl.h>
#include <unistd.h>
#include <netinet/in.h>
int main()
{
  int        rcd ;
  struct sockaddr_un  server_sockaddr ;
  int        backlog ;
  ushort        ci ;
  int        watch_fd_list[3] ;
  fd_set        catch_fd_set ;
  fd_set        watchset ;
  int    new_cli_fd ;
  int   maxfd;
  int    socklen ,server_len;
  struct sockaddr_un  cli_sockaddr ;
  struct {
    char  module_id ;  /* Module ID    */
    int  cli_sock_fd ;  /* Socket ID    */
  } cli_info_t[2] ;

  for (ci=0;ci<=1;ci++)
    cli_info_t[ci].cli_sock_fd=-1;

  for (ci=0;ci<=2;ci++)
    watch_fd_list[ci]=-1;  

  int server_sockfd,client_sockfd;

  server_sockfd = socket( AF_UNIX, SOCK_STREAM, 0 ) ;
  server_sockaddr.sun_family = AF_UNIX ;
  strcpy( server_sockaddr.sun_path, "server_socket" ) ;
  server_len=sizeof(server_sockaddr);
  rcd = bind( server_sockfd, ( struct sockaddr * )&server_sockaddr, server_len ) ;

  backlog = 5 ;
  rcd = listen( server_sockfd, backlog ) ;
  printf("SERVER::Server is  waitting on socket=%d \n",server_sockfd);
  watch_fd_list[0]=server_sockfd;
  FD_ZERO( &watchset ) ;
  FD_SET( server_sockfd, &watchset ) ;
  maxfd=watch_fd_list[0];

在上面的程序中,Server生成listening Socket(server_sockfd),初始化Socket监视集合(watchset),并将listening Socket放入Socket监视集合中。

   while (1){
  char ch;
  int fd;
  int nread;

  catch_fd_set=watchset;
  rcd = select( maxfd+1, &catch_fd_set, NULL, NULL, (struct timeval *)0 ) ;
   ``` 

在上面的程序中,Server运用系统调用函数 select来实时检查是否有数据到达Socket监视集合中的任何一个socket。
    if ( rcd < 0 ) {
  printf("SERVER::Server 5 \n");
  exit(1);
}
if ( FD_ISSET( server_sockfd, &catch_fd_set ) ) {
  socklen = sizeof( cli_sockaddr ) ;
  new_cli_fd = accept( server_sockfd, ( struct sockaddr * )
    &( cli_sockaddr ), &socklen ) ;
  printf(" SERVER::open communication with  Client %s on socket %d\n", 
       cli_sockaddr.sun_path,new_cli_fd);  

  for (ci=1;ci<=2;ci++){
    if(watch_fd_list[ci] != -1) continue;
    else{  
      watch_fd_list[ci] = new_cli_fd;
      break;
    }  
  }  
  FD_SET(new_cli_fd , &watchset ) ;
  if ( maxfd < new_cli_fd ) {
    maxfd = new_cli_fd ;
  }

  for ( ci=0;ci<=1;ci++){
    if(cli_info_t[ci].cli_sock_fd == -1) {
      cli_info_t[ci].module_id=cli_sockaddr.sun_path[0];
      cli_info_t[ci].cli_sock_fd=new_cli_fd;
      break;
    }    
  }

  continue;  
}

在上面的程序中,Server运用系统调用函数FD_ISSET来检查是否有客户端的连接请求到达Listening Socket, 如果返回值大于0,Server生成一个新的通信Socket (new_cli_fd)与客户端连接。将新生成的通信Socket放入Socket监视集合中(FD_SET)。将客户端的信息(ID号和Socket描述号)保存在注册表cli_info_t中

    for ( ci = 1; ci<=2 ; ci++ ) {
      int      dst_fd = -1 ;
      char      dst_module_id;
      char       src_module_id;
      int    i;
      if (watch_fd_list[ ci ]==-1) continue;
      if ( !FD_ISSET( watch_fd_list[ ci ], &catch_fd_set ) ) {
        continue ;
      }
      ioctl(watch_fd_list[ ci ],FIONREAD,&nread);
      if (nread==0){
        continue;
      }  
      read( watch_fd_list[ ci ], &dst_module_id, 1 ) ;
      for (i=0;i<=1;i++){
        if(cli_info_t[i].module_id == dst_module_id) 
          dst_fd=  cli_info_t[i].cli_sock_fd;  
         if(cli_info_t[i].cli_sock_fd==watch_fd_list[ ci ]) 
        src_module_id=  cli_info_t[i].module_id;    
      }
      read( watch_fd_list[ ci ], &ch, 1 ) ;
      printf("SERVER::char=%c to  Client %c on socket%d\n",ch, dst_module_id,dst_fd);  
      write(dst_fd,&src_module_id, 1 ) ;
      write(dst_fd,&ch, 1 ) ;
    }
  }  
}

在上面的程序中,如果有数据到达某个通信Socket,Server则读出数据并取出收信客户端ID号。在ID登记表中找到收信客户端对应的Socket描述号。并将数据通过对应Socket传送到收信客户端
给出客户端 ClientA的程序
ClientB的程序只需将 char dst_module_id=’B’; 改为char dst_module_id=’A’; char ch=’1’; 改为char char ch=’2’;既可。

#include  <sys/types.h>
#include <sys/socket.h>
#include <stdio.h>
#include <sys/un.h>
#include <unistd.h>
#include <netinet/in.h>
#include <arpa/inet.h>
int main(){

 int client_sockfd;
 int len;
 struct sockaddr_un server_sockaddr,cli_sockaddr;
 int result;
 char dst_module_id='B';
 char ch='1';
 char src_module_id;


 client_sockfd= socket(AF_UNIX,SOCK_STREAM,0);

 cli_sockaddr.sun_family = AF_UNIX ;
 strcpy( cli_sockaddr.sun_path, "A" ) ;
 bind(client_sockfd,(struct sockaddr * )&cli_sockaddr, sizeof( cli_sockaddr ) ) ;
 server_sockaddr.sun_family=AF_UNIX;
 strcpy( server_sockaddr.sun_path, "server_socket" ) ;
 len=sizeof(server_sockaddr);

 result = connect(client_sockfd,( struct sockaddr * )&server_sockaddr,len);
 if (result <0){
  printf("ClientA::error on connecting \n"); 
  exit(1);
 }

 printf("ClientA::succeed in connecting with server\n");
 sleep(10);
 write(client_sockfd,&dst_module_id,1);
 write(client_sockfd,&ch,1); 
 read (client_sockfd,&src_module_id,1); 
 read (client_sockfd,&ch,1); 
 printf("ClientA::char from  Client %c =%c\n", src_module_id,ch); 
 close (client_sockfd);

} 

下面是样本程序的执行结果
[root@zhou test]# ./server &
[3] 4301
[root@zhou test]# SERVER::Server is waitting on socket=3
./clientA & ./clientB &
[4] 4302
[5] 4303
ClientA::succeed in connecting with server
SERVER::open communication with Client A on socket 4
[root@zhou test]# SERVER::open communication with Client B on socket 5
ClientB::succeed in connecting with server
SERVER::char=1 to Client B on socket5
ClientB::char from Client A =1
SERVER::char=2 to Client A on socket4
ClientA::char from Client B =2

8. MYSQL字符集
前一段时间,一直被mysql的字符集困扰,今天就这方面的知识总结一下.
MySQL的字符集支持(Character Set Support)有两个方面:
字符集(Character set)和排序方式(Collation)。
对于字符集的支持细化到四个层次:
服务器(server),数据库(database),数据表(table)和连接(connection)。

1.MySQL默认字符集
MySQL对于字符集的指定可以细化到一个数据库,一张表,一列,应该用什么字符集。
但是,传统的程序在创建数据库和数据表时并没有使用那么复杂的配置,它们用的是默认的配置,那么,默认的配置从何而来呢?
(1)编译MySQL 时,指定了一个默认的字符集,这个字符集是 latin1;
(2)安装MySQL 时,可以在配置文件 (my.ini) 中指定一个默认的的字符集,如果没指定,这个值继承自编译时指定的;
(3)启动mysqld 时,可以在命令行参数中指定一个默认的的字符集,如果没指定,这个值继承自配置文件中的配置,此时 character_set_server 被设定为这个默认的字符集;
(4)当创建一个新的数据库时,除非明确指定,这个数据库的字符集被缺省设定为character_set_server;
(5)当选定了一个数据库时,character_set_database 被设定为这个数据库默认的字符集;
(6)在这个数据库里创建一张表时,表默认的字符集被设定为 character_set_database,也就是这个数据库默认的字符集;
(7)当在表内设置一栏时,除非明确指定,否则此栏缺省的字符集就是表默认的字符集;
简单的总结一下,如果什么地方都不修改,那么所有的数据库的所有表的所有栏位的都用 latin1 存储,不过我们如果安装 MySQL,一般都会选择多语言支持,也就是说,安装程序会自动在配置文件中把 default_character_set 设置为 UTF-8,这保证了缺省情况下,所有的数据库的所有表的所有栏位的都用 UTF-8 存储。

2.查看默认字符集(默认情况下,mysql的字符集是latin1(ISO_8859_1)
通常,查看系统的字符集和排序方式的设定可以通过下面的两条命令:
mysql> SHOW VARIABLES LIKE ‘character%’;
+————————–+———————————+
| Variable_name | Value |
+————————–+———————————+
| character_set_client | latin1 |
| character_set_connection | latin1 |
| character_set_database | latin1 |
| character_set_filesystem | binary |
| character_set_results | latin1 |
| character_set_server | latin1 |
| character_set_system | utf8 |
| character_sets_dir | D:”mysql-5.0.37”share”charsets” |
+————————–+———————————+

mysql> SHOW VARIABLES LIKE ‘collation_%’;
+———————-+—————–+
| Variable_name | Value |
+———————-+—————–+
| collation_connection | utf8_general_ci |
| collation_database | utf8_general_ci |
| collation_server | utf8_general_ci |
+———————-+—————–+

3.修改默认字符集
(1) 最简单的修改方法,就是修改mysql的my.ini文件中的字符集键值,
如 default-character-set = utf8
character_set_server = utf8
修改完后,重启mysql的服务,service mysql restart
使用 mysql> SHOW VARIABLES LIKE ‘character%’;查看,发现数据库编码均已改成utf8
+————————–+———————————+
| Variable_name | Value |
+————————–+———————————+
| character_set_client | utf8 |
| character_set_connection | utf8 |
| character_set_database | utf8 |
| character_set_filesystem | binary |
| character_set_results | utf8 |
| character_set_server | utf8 |
| character_set_system | utf8 |
| character_sets_dir | D:”mysql-5.0.37”share”charsets” |
+————————–+———————————+

(2) 还有一种修改字符集的方法,就是使用mysql的命令
mysql> SET character_set_client = utf8 ;
mysql> SET character_set_connection = utf8 ;
mysql> SET character_set_database = utf8 ;
mysql> SET character_set_results = utf8 ;
mysql> SET character_set_server = utf8 ;

mysql> SET collation_connection = utf8 ;
mysql> SET collation_database = utf8 ;
mysql> SET collation_server = utf8 ;

一般就算设置了表的默认字符集为utf8并且通过UTF-8编码发送查询,你会发现存入数据库的仍然是乱码。问题就出在这个connection连接层上。解决方法是在发送查询前执行一下下面这句:
SET NAMES ‘utf8’;
它相当于下面的三句指令:
SET character_set_client = utf8;
SET character_set_results = utf8;
SET character_set_connection = utf8;

总结:
因此,使用什么数据库版本,不管是3.x,还是4.0.x还是4.1.x,其实对我们来说不重要,重要的有二:
1) 正确的设定数据库编码.MySQL4.0以下版本的字符集总是默认ISO8859-1,MySQL4.1在安装的时候会让你选择。如果你准备使用UTF- 8,那么在创建数据库的时候就要指定好UTF-8(创建好以后也可以改,4.1以上版本还可以单独指定表的字符集)
2) 正确的设定数据库connection编码.设置好数据库的编码后,在连接数据库时候,应该指定connection的编码,比如使用jdbc连接时,指定连接为utf8方式

9. 测特定指令执行时所需消耗的时间及系统资源
名称: time
  
  使用权限: 所有使用者
  
  使用方式: time [options] COMMAND
[arguments]
  
  说明: time 指令的用途,在于量测特定指令执行时所需消耗的时间及系统资源等资讯。例如 CPU
时间、记忆体、输入输出等等。需要特别注意的是,部分资讯在 Linux 上显示不出来。这是因为在 Linux 上部分资源的分配函式与 time
指令所预设的方式并不相同,以致于 time 指令无法取得这些资料。
  
  参数:
  
  -o or
–output=FILE
  设定结果输出档。这个选项会将 time 的输出写入 所指定的档案中。如果档案已经存在,系统将覆写其内容。
  -a or –append
  配合 -o 使用,会将结果写到档案的末端,而不会覆盖掉原来的内容。
  -f FORMAT or
–format=FORMAT
  以 FORMAT 字串设定显示方式。当这个选项没有被设定的时候,会用系统预设的格式。不过你可以用环境变数 time
来设定这个格式,如此一来就不必每次登入系统都要设定一次。
  一般设定上,你可以用
   ’ ’
  表示跳栏,或者是用
  

  ’
  表示换行。每一项资料要用 % 做为前导。如果要在字串中使用百分比符号,就用 。(学过 C 语言的人大概会觉得很熟悉)
  time 指令可以显示的资源有四大项,分别是:
  
  Time resources
  Memory resources
  IO resources
  Command info
  
  详细的内容如下:
  
  Time
Resources
  E 执行指令所花费的时间,格式是:[hour]:minute:second。请注意这个数字并不代表实际的 CPU 时间。
  e 执行指令所花费的时间,单位是秒。请注意这个数字并不代表实际的 CPU 时间。
  S 指令执行时在核心模式(kernel
mode)所花费的时间,单位是秒。
  U 指令执行时在使用者模式(user mode)所花费的时间,单位是秒。
  P 执行指令时 CPU
的占用比例。其实这个数字就是核心模式加上使用者模式的 CPU 时间除以总时间。
  
  Memory Resources
  M
执行时所占用的实体记忆体的最大值。单位是 KB
  t 执行时所占用的实体记忆体的平均值,单位是 KB
  K
执行程序所占用的记忆体总量(stack+data+text)的平均大小,单位是 KB
  D 执行程序的自有资料区(unshared data
area)的平均大小,单位是 KB
  p 执行程序的自有堆叠(unshared stack)的平均大小,单位是 KB
  X
执行程序间共享内容(shared text)的平均值,单位是 KB
  Z 系统记忆体页的大小,单位是 byte。对同一个系统来说这是个常数
  
  IO Resources
  F
此程序的主要记忆体页错误发生次数。所谓的主要记忆体页错误是指某一记忆体页已经置换到置换档(swap
file)中,而且已经分配给其他程序。此时该页的内容必须从置换档里再读出来。
  R
此程序的次要记忆体页错误发生次数。所谓的次要记忆体页错误是指某一记忆体页虽然已经置换到置换档中,但尚未分配给其他程序。此时该页的内容并未被破坏,不必从置换档里读出来
  W 此程序被交换到置换档的次数
  c 此程序被强迫中断(像是分配到的 CPU 时间耗尽)的次数
  w
此程序自愿中断(像是在等待某一个 I/O 执行完毕,像是磁碟读取等等)的次数
  I 此程序所输入的档案数
  O 此程序所输出的档案数
  r 此程序所收到的 Socket Message
  s 此程序所送出的 Socket Message
  k 此程序所收到的信号
( Signal )数量
  
  Command Info
  C 执行时的参数以及指令名称
  x 指令的结束代码 (
Exit Status )
  
  -p or –portability
  这个选项会自动把显示格式设定成为:
  real
%e
  user %U
  sys %S
  这么做的目的是为了与 POSIX 规格相容。
  -v or –verbose
  这个选项会把所有程序中用到的资源通通列出来,不但如一般英文语句,还有说明。对不想花时间去熟习格式设定或是刚刚开始接触这个指令的人相当有用。
  
  范例:
  利用下面的指令
   time -v ps -aux
  
  我们可以获得执行 ps
-aux 的结果和所花费的系统资源。如下面所列的资料:
   USER PID %CPU %MEM VSZ RSS TTY STAT START
TIME COMMAND
  root 1 0.0 0.4 1096 472 ? S Apr19 0:04 init
  root 2 0.0
0.0 0 0 ? SW Apr19 0:00 [kflushd]
  root 3 0.0 0.0 0 0 ? SW Apr19 0:00
[kpiod]
  ……
  root 24269 0.0 1.0 2692 996 pts/3 R 12:16 0:00 ps
-aux
  
   Command being timed: “ps -aux”
   User time (seconds):
0.05
   System time (seconds): 0.06
   Percent of CPU this job got: 68%
   Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.16
   Average
shared text size (kbytes): 0
   Average unshared data size (kbytes): 0
   Average stack size (kbytes): 0
   Average total size (kbytes): 0
   Maximum resident set size (kbytes): 0
   Average resident set size
(kbytes): 0
   Major (requiring I/O) page faults: 238
   Minor
(reclaiming a frame) page faults: 46
   Voluntary context switches: 0
  
Involuntary context switches: 0
   Swaps: 0
   File system inputs: 0
   File system outputs: 0
   Socket messages sent: 0
   Socket
messages received: 0
   Signals delivered: 0
   Page size (bytes): 4096
   Exit status: 0

10. 细说 #pragma pack(n)
在C语言中,结构是一种复合数据类型,其构成元素既可以是基本数据类型(如int、long、float等)的变量,也可以是一些复合数据类型(如数组、结构、联合等)的数据单元。在结构中,编译器为结构的每个成员按其自然对界(alignment)条件分配空间。各个成员按照它们被声明的顺序在内存中顺序存储,第一个成员的地址和整个结构的地址相同。
例如,下面的结构各成员空间分配情况:

struct test 
{
char x1;
short x2;
float x3;
char x4;
};

结构的第一个成员x1,其偏移地址为0,占据了第1个字节。第二个成员x2为short类型,其起始地址必须2字节对界,因此,编译器在x2和x1之间填充了一个空字节。结构的第三个成员x3和第四个成员x4恰好落在其自然对界地址上,在它们前面不需要额外的填充字节。在test结构中,成员x3要求4字节对界,是该结构所有成员中要求的最大对界单元,因而test结构的自然对界条件为4字节,编译器在成员x4后面填充了3个空字节。整个结构所占据空间为12字节。
更改C编译器的缺省字节对齐方式
在缺省情况下,C编译器为每一个变量或是数据单元按其自然对界条件分配空间。一般地,可以通过下面的方法来改变缺省的对界条件:
· 使用伪指令#pragma pack (n),C编译器将按照n个字节对齐。
· 使用伪指令#pragma pack (),取消自定义字节对齐方式。
另外,还有如下的一种方式:
· __attribute((aligned (n))),让所作用的结构成员对齐在n字节自然边界上。如果结构中有成员的长度大于n,则按照最大成员的长度来对齐。
· attribute ((packed)),取消结构在编译过程中的优化对齐,按照实际占用字节数进行对齐。
以上的n = 1, 2, 4, 8, 16… 第一种方式较为常见。
( via http://blog.csdn.net/wenddy112/articles/300583.aspx )
下面有一道在 CSDN论坛 上讨论火热的题:
Intel和微软和本公司同时出现的面试题

#pragma pack(8)
struct s1{
short a;
long b;
};
struct s2{
char c;
s1 d;
long long e;
};
#pragma pack()


1.sizeof(s2) = ?
2.s2的c后面空了几个字节接着是d?
感谢 redleaves(ID最吊的网友) 的解答,结果如下:
sizeof(S2)结果为24.
成员对齐有一个重要的条件,即每个成员分别对齐.即每个成员按自己的方式对齐.
也就是说上面虽然指定了按8字节对齐,但并不是所有的成员都是以8字节对齐.其对齐的规则是,每个成员按其类型的对齐参数(通常是这个类型的大小)和指定对齐参数(这里是8字节)中较小的一个对齐.并且结构的长度必须为所用过的所有对齐参数的整数倍,不够就补空字节.
S1中,成员a是1字节默认按1字节对齐,指定对齐参数为8,这两个值中取1,a按1字节对齐;成员b是4个字节,默认是按4字节对齐,这时就按4字节对齐,所以sizeof(S1)应该为8;
S2中,c和S1中的a一样,按1字节对齐,而d 是个结构,它是8个字节,它按什么对齐呢?对于结构来说,它的默认对齐方式就是它的所有成员使用的对齐参数中最大的一个,S1的就是4.所以,成员d就是按4字节对齐.成员e是8个字节,它是默认按8字节对齐,和指定的一样,所以它对到8字节的边界上,这时,已经使用了12个字节了,所以又添加了4个字节的空,从第16个字节开始放置成员e.这时,长度为24,已经可以被8(成员e按8字节对齐)整除.这样,一共使用了24个字节.
a b
S1的内存布局:11**,1111,
c S1.a S1.b d
S2的内存布局:1***,11**,1111,****11111111
这里有三点很重要:
1.每个成员分别按自己的方式对齐,并能最小化长度
2.复杂类型(如结构)的默认对齐方式是它最长的成员的对齐方式,这样在成员是复杂类型时,可以最小化长度
3.对齐后的长度必须是成员中最大的对齐参数的整数倍,这样在处理数组时可以保证每一项都边界对齐
补充一下,对于数组,比如:
char a[3];这种,它的对齐方式和分别写3个char是一样的.也就是说它还是按1个字节对齐.
如果写: typedef char Array3[3];
Array3这种类型的对齐方式还是按1个字节对齐,而不是按它的长度.
不论类型是什么,对齐的边界一定是1,2,4,8,16,32,64….中的一个.

11. new 与 malloc
1. new 是c++中的操作符,malloc是c 中的一个函数

  1. new 不止是分配内存,而且会调用类的构造函数,同理delete会调用类的析构函数,而malloc则只分配内存,不会进行初始化类成员的工作,同样free也不会调用析构函数

  2. 内存泄漏对于malloc或者new都可以检查出来的,区别在于new可以指明是那个文件的那一行,而malloc没有这些信息。

  3. new 和 malloc效率比较
    new 有三个字母, malloc有六个字母
    new可以认为是malloc加构造函数的执行。
    new出来的指针是直接带类型信息的。
    而malloc返回的都是void指针。
    一:new delete 是运算符,malloc,free是函数
    malloc与free是C++/C语言的标准库函数,new/delete是C++的运算符。它们都可用于申请动态内存和释放内存。
    对于非内部数据类型的对象而言,光用maloc/free无法满足动态对象的要求。对象在创建的同时要自动执行构造函数,对象在消亡之前要自动执行析构函数。由于malloc/free是库函数而不是运算符,不在编译器控制权限之内,不能够把执行构造函数和析构函数的任务强加于malloc/free。
    因此C++语言需要一个能完成动态内存分配和初始化工作的运算符new,以及一个能完成清理与释放内存工作的运算符delete。注意new/delete不是库函数。
    我们先看一看malloc/free和new/delete如何实现对象的动态内存管理,见示例。

class Obj
{
public :
          Obj(void){ cout << “Initialization” << endl; }
~Obj(void){ cout << “Destroy” << endl; }
void      Initialize(void){ cout << “Initialization” << endl; }
void      Destroy(void){ cout << “Destroy” << endl; }
};

void UseMallocFree(void)
{
      Obj    *a = (obj *)malloc(sizeof(obj));     // 申请动态内存
      a->Initialize();                          // 初始化
      //…
      a->Destroy();     // 清除工作
      free(a);          // 释放内存
}

void UseNewDelete(void)
{
      Obj    *a = new Obj;    // 申请动态内存并且初始化
      //…
      delete a;             // 清除并且释放内存
}

示例用malloc/free和new/delete如何实现对象的动态内存管理
类Obj的函数Initialize模拟了构造函数的功能,函数Destroy模拟了析构函数的功能。函数UseMallocFree中,由于malloc/free不能执行构造函数与析构函数,必须调用成员函数Initialize和Destroy来完成初始化与清除工作。函数UseNewDelete则简单得多。
所以我们不要企图用malloc/free来完成动态对象的内存管理,应该用new/delete。由于内部数据类型的“对象”没有构造与析构的过程,对它们而言malloc/free和new/delete是等价的。
既然new/delete的功能完全覆盖了malloc/free,为什么C++不把malloc/free淘汰出局呢?这是因为C++程序经常要调用C函数,而C程序只能用malloc/free管理动态内存。
如果用free释放“new创建的动态对象”,那么该对象因无法执行析构函数而可能导致程序出错。如果用delete释放“malloc申请的动态内存”,理论上讲程序不会出错,但是该程序的可读性很差。所以new/delete必须配对使用,malloc/free也一样。
二:new delete在实现上其实调用了malloc,free函数。
三:new operator除了分配内存,还要调用构造函数。
malloc函数只是负责分配内存

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值