分类:
2008-10-13 16:14:15
#include嘿嘿,这个bug可不简单,它会导致偶的系统驱动层的所有实体全部崩溃掉。偶是用的世界著名的A公司的OS,这个OS很多公司都在用。这段代码是偶写的代码的一个简化版。a[]这个数组是显示的缓冲区,我需要将它的内容trace到串口。因为a[]中的数据量很大,如果我极为频繁的访问串口的话,会导致串口阻塞,trace数据丢失,看不到我要看的数据。所以我把数据分了组,用strcat()来合并数据,一组一组的往串口发。#include int main(int argc, char *argv[]) { #define MAX (100) #define START (10000) char buf[512], tmp[32]; int a[MAX]; int count = 0; int i; printf("%d %d %d \n",a[0],a[1],a[2]); //initialize an array for testing for ( i = START; i<START + MAX; ++i) { a[i - START] = i; } for ( i=0; i<MAX; ++i) { sprintf(tmp, "0x%.4X,", i); strcat(buf, tmp); if (count++ == 9) { //print result, output to standard io stream for samulation. printf("%s\n", buf); buf[0] = '\0'; count = 0; } } return 0; }
#include问题的关键在于传递给strcat(dst,src)的两个参数都要是以'\0'结尾的。而我第一次传递给strcat()的第一个参数buf[]没有初始化,导致了buf的内容都非0,从而引起缓冲区溢出的错误。#include int main(int argc, char *argv[]) { #define MAX (100) #define START (10000) char buf[512], tmp[32]; int a[MAX]; int count = 0; int i; printf("%d %d %d \n",a[0],a[1],a[2]); //initialize an array for testing for ( i = START; i<START + MAX; ++i) { a[i - START] = i; } buf[0] = '\0'; ////////////////////////////////////////// here ! for ( i=0; i<MAX; ++i) { sprintf(tmp, "0x%.4X,", i); strcat(buf, tmp); if (count++ == 9) { //print result, output to standard io stream for samulation. printf("%s\n", buf); buf[0] = '\0'; count = 0; } } return 0; }
char * __cdecl strcat ( char * dst, const char * src ) { char * cp = dst; while( *cp ) cp++; /* find end of dst */ while( *cp++ = *src++ ) ; /* Copy src to end of dst */ return( dst ); /* return dst */ }在find end of dst这一步,程序会从dst的起始地址开始,去找一个'\0',并且只有找到这个'\0',代码才会结束。Okey,问题明了了。由于我没有初始化buf[],而系统随便分配一块内存给它,里面可能还不为0,它就不停的跑下去了。就算它停了下来,第二步的copy src to end of dst也会接着写下去,如果此刻超出了buf的范围,就会把src中的内容写到不该写的位置上去,这样就破坏了其他的进程的代码和数据,系统就崩溃掉了。
//////// Linux 0.11 /////////// extern inline char * strcat(char * dest,const char * src) { __asm__("cld\n\t" "repne\n\t" "scasb\n\t" "decl %1\n" "1:\tlodsb\n\t" "stosb\n\t" "testb %%al,%%al\n\t" "jne 1b" ::"S" (src),"D" (dest),"a" (0),"c" (0xffffffff)); return dest; }Linux 2.4.26的实现:
///// LINUX 2.4.26 ///// #ifndef __HAVE_ARCH_STRCAT /** * strcat - Append one %NUL-terminated string to another * @dest: The string to be appended to * @src: The string to append to it */ char * strcat(char * dest, const char * src) { char *tmp = dest; while (*dest) dest++; while ((*dest++ = *src++) != '\0') ; return tmp; } #endifGNU glibc的实现:
/* Copyright (C) 1991, 1997, 2003 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #include#include /* Append SRC on the end of DEST. */ char * strcat (dest, src) char *dest; const char *src; { char *s1 = dest; const char *s2 = src; reg_char c; /* Find the end of the string. */ do c = *s1++; while (c != '\0'); /* Make S1 point before the next character, so we can increment it while memory is read (wins on pipelined cpus). */ s1 -= 2; do { c = *s2++; *++s1 = c; } while (c != '\0'); return dest; } libc_hidden_builtin_def (strcat)#undef strcat
page ,132 title strcat - concatenate (append) one string to another ;*** ;strcat.asm - contains strcat() and strcpy() routines ; ; Copyright (c) Microsoft Corporation. All rights reserved. ; ;Purpose: ; STRCAT concatenates (appends) a copy of the source string to the ; end of the destination string, returning the destination string. ; ;******************************************************************************* .xlist include cruntime.inc .list page ;*** ;char *strcat(dst, src) - concatenate (append) one string to another ; ;Purpose: ; Concatenates src onto the end of dest. Assumes enough ; space in dest. ; ; Algorithm: ; char * strcat (char * dst, char * src) ; { ; char * cp = dst; ; ; while( *cp ) ; ++cp; /* Find end of dst */ ; while( *cp++ = *src++ ) ; ; /* Copy src to end of dst */ ; return( dst ); ; } ; ;Entry: ; char *dst - string to which "src" is to be appended ; const char *src - string to be appended to the end of "dst" ; ;Exit: ; The address of "dst" in EAX ; ;Uses: ; EAX, ECX ; ;Exceptions: ; ;******************************************************************************* page ;*** ;char *strcpy(dst, src) - copy one string over another ; ;Purpose: ; Copies the string src into the spot specified by ; dest; assumes enough room. ; ; Algorithm: ; char * strcpy (char * dst, char * src) ; { ; char * cp = dst; ; ; while( *cp++ = *src++ ) ; ; /* Copy src over dst */ ; return( dst ); ; } ; ;Entry: ; char * dst - string over which "src" is to be copied ; const char * src - string to be copied over "dst" ; ;Exit: ; The address of "dst" in EAX ; ;Uses: ; EAX, ECX ; ;Exceptions: ;******************************************************************************* CODESEG % public strcat, strcpy ; make both functions available strcpy proc push edi ; preserve edi mov edi,[esp+8] ; edi points to dest string jmp short copy_start strcpy endp align 16 strcat proc .FPO ( 0, 2, 0, 0, 0, 0 ) mov ecx,[esp+4] ; ecx -> dest string push edi ; preserve edi test ecx,3 ; test if string is aligned on 32 bits je short find_end_of_dest_string_loop dest_misaligned: ; simple byte loop until string is aligned mov al,byte ptr [ecx] add ecx,1 test al,al je short start_byte_3 test ecx,3 jne short dest_misaligned align 4 find_end_of_dest_string_loop: mov eax,dword ptr [ecx] ; read 4 bytes mov edx,7efefeffh add edx,eax xor eax,-1 xor eax,edx add ecx,4 test eax,81010100h je short find_end_of_dest_string_loop ; found zero byte in the loop mov eax,[ecx - 4] test al,al ; is it byte 0 je short start_byte_0 test ah,ah ; is it byte 1 je short start_byte_1 test eax,00ff0000h ; is it byte 2 je short start_byte_2 test eax,0ff000000h ; is it byte 3 je short start_byte_3 jmp short find_end_of_dest_string_loop ; taken if bits 24-30 are clear and bit ; 31 is set start_byte_3: lea edi,[ecx - 1] jmp short copy_start start_byte_2: lea edi,[ecx - 2] jmp short copy_start start_byte_1: lea edi,[ecx - 3] jmp short copy_start start_byte_0: lea edi,[ecx - 4] ; jmp short copy_start ; edi points to the end of dest string. copy_start:: mov ecx,[esp+0ch] ; ecx -> sorc string test ecx,3 ; test if string is aligned on 32 bits je short main_loop_entrance src_misaligned: ; simple byte loop until string is aligned mov dl,byte ptr [ecx] add ecx,1 test dl,dl je short byte_0 mov [edi],dl add edi,1 test ecx,3 jne short src_misaligned jmp short main_loop_entrance main_loop: ; edx contains first dword of sorc string mov [edi],edx ; store one more dword add edi,4 ; kick dest pointer main_loop_entrance: mov edx,7efefeffh mov eax,dword ptr [ecx] ; read 4 bytes add edx,eax xor eax,-1 xor eax,edx mov edx,[ecx] ; it is in cache now add ecx,4 ; kick dest pointer test eax,81010100h je short main_loop ; found zero byte in the loop ; main_loop_end: test dl,dl ; is it byte 0 je short byte_0 test dh,dh ; is it byte 1 je short byte_1 test edx,00ff0000h ; is it byte 2 je short byte_2 test edx,0ff000000h ; is it byte 3 je short byte_3 jmp short main_loop ; taken if bits 24-30 are clear and bit ; 31 is set byte_3: mov [edi],edx mov eax,[esp+8] ; return in eax pointer to dest string pop edi ret byte_2: mov [edi],dx mov eax,[esp+8] ; return in eax pointer to dest string mov byte ptr [edi+2],0 pop edi ret byte_1: mov [edi],dx mov eax,[esp+8] ; return in eax pointer to dest string pop edi ret byte_0: mov [edi],dl mov eax,[esp+8] ; return in eax pointer to dest string pop edi ret strcat endp end
-------------
乾坤一笑 写于2005年6月18 日 转载请标明出处和原文链接
/////// vc6.0 source //////////// char * __cdecl strncpy ( char * dest, const char * source, size_t count ) { char *start = dest; while (count && (*dest++ = *source++)) /* copy string */ count--; if (count) /* pad out with zeroes */ while (--count) *dest++ = '\0'; return(start); }
你看MSDN看的很细,可惜的是没有正确理解“NULL结尾的字符串”的含义。MSDN上所谓的Null-terminated string 其实就是着的是“以'\0'结尾的字符串”。要理解这个概念要说到三个方面:
一、NULL是什么?
纵观VC6.0的库代码,有22处定义了NULL宏,但是所有定义都是一模一样的:
#undef NULL #ifndef NULL #ifdef __cplusplus #define NULL 0 #else /* __cplusplus */ #define NULL ((void *)0) #endif /* __cplusplus */ #endif /* NULL */
二、'\0'是什么?
'\0'是C语言定义的用'\'加8进制ASCII码来表示字符的一种方法,'\0'就是表示一个ASCII码值为0的字符。同样的,你用'\x0'来表示也可以,这是用16进制的ASCII码来表示,虽然很不常见。
三、0值的意义何在?
0是一个整数。由于各种标准转换,0可以被用于作为任意整型、浮点类型、指针。0的类型将用上下文来确定。典型情况下0被表示为一个适当大小的全零二进制位的模式。所以,无论NULL是定义为常数0还是((void *)0)这个零指针,NULL都是指的是0值,而不是非0值。而字符的'\0'和整数的0也可以通过转型来相互转换。
综上所述,NULL就是指的是0值,Null-terminated string就是以0值结尾的string,也就是以'\0'。其实C语言中的变量只有4中char、int、float、poiner四大类。所谓的字符串只是一堆char的后面加上一个0而已。