[英]Implementing a new strcpy function redefines the library function strcpy?
據說我們可以編寫多個聲明但只能編寫一個定義。 現在,如果我使用相同的原型實現我自己的strcpy函數:
char * strcpy ( char * destination, const char * source );
那么我不是在重新定義現有的庫函數嗎? 這不應該顯示錯誤嗎? 或者它是否以某種方式與庫函數以目標代碼形式提供的事實有關?
編輯:在我的機器上運行以下代碼說“分段故障(核心轉儲)”。 我正在使用linux並且已經編譯而沒有使用任何標志。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *strcpy(char *destination, const char *source);
int main(){
char *s = strcpy("a", "b");
printf("\nThe function ran successfully\n");
return 0;
}
char *strcpy(char *destination, const char *source){
printf("in duplicate function strcpy");
return "a";
}
請注意,我不是要嘗試實現該功能。 我只是想重新定義一個函數並詢問后果。
編輯2:在應用Mats建議的更改后,程序不再提供分段錯誤,盡管我仍在重新定義函數。
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char *strcpy(char *destination, const char *source);
int main(){
char *s = strcpy("a", "b");
printf("\nThe function ran successfully\n");
return 0;
}
char *strcpy(char *destination, const char *source){
printf("in duplicate function strcpy");
return "a";
}
C11(ISO / IEC 9899:201x)§7.1.3 保留標識符
- 如果包含任何相關標頭,則保留以下任何子條款中的每個宏名稱(包括未來的庫方向)以供指定使用; 除非另有明確說明。
- 以下任何子條款中包含外部鏈接的所有標識符(包括未來的庫方向)始終保留用作具有外部鏈接的標識符。
- 如果包含任何相關標頭,則保留下列任何子條款(包括未來庫方向)中列出的具有文件范圍的每個標識符,以用作宏名稱和具有相同名稱空間的文件范圍的標識符。
如果程序在保留它的上下文中聲明或定義標識符,或者將保留標識符定義為宏名稱,則行為是未定義的。 請注意,這並不意味着你不能這樣做,正如這篇文章所示,它可以在gcc和glibc中完成。
glibc§1.3.3保留名稱證明了一個更明確的原因:
來自ISO C標准的所有庫類型,宏,變量和函數的名稱都是無條件保留的; 您的程序可能不會重新定義這些名稱。 如果您的程序明確包含定義或聲明它們的頭文件,則保留所有其他庫名。 這些限制有幾個原因:
如果您使用名為exit的函數執行與標准退出函數完全不同的操作,那么閱讀代碼的其他人可能會非常困惑。 防止這種情況有助於使您的程序更易於理解,並有助於模塊化和可維護性。
它避免了用戶意外重新定義其他庫函數調用的庫函數的可能性。 如果允許重新定義,那些其他功能將無法正常工作。
它允許編譯器在調用這些函數時進行任何特殊的優化,而不會被用戶重新定義。 一些庫設施,例如用於處理可變參數(參見Variadic函數)和非本地出口(參見非本地退出)的設施,實際上需要C編譯器方面的大量合作,並且相對於實現,編譯器可能更容易將它們視為語言的內置部分。
這幾乎肯定是因為你傳遞的是一個“字符串文字”的目的地。
char * s = strcpy(“a”,“b”);
隨着編譯器知道“我可以執行strcpy
inline”,所以你的函數永遠不會被調用。
您正試圖在字符串文字"a"
上復制"b"
"a"
,但這不起作用。
做一個char a[2];
和strcpy(a, "b");
它會運行 - 它可能不會調用你的strcpy
函數,因為即使你沒有可用的優化,編譯器也會內聯小的strcpy
。
將試圖修改不可修改的內存放在一邊,請記住,正式不允許重新定義標准庫函數。
但是,在某些實現中,您可能會注意到為標准庫函數提供另一個定義不會觸發通常的“多重定義”錯誤。 發生這種情況是因為在這種實現中,標准庫函數被定義為所謂的“弱符號”。 例如,GCC標准庫就是眾所周知的。
這樣做的直接后果是,當您使用外部鏈接定義自己的標准庫函數“版本”時,您的定義將覆蓋整個程序的“弱”標准定義。 您會注意到,不僅您的代碼現在調用您的函數版本,而且所有預編譯的[第三方]庫中的所有類也都會調度到您的定義中。 它旨在作為一項功能,但您必須注意它,以避免無意中“使用”此功能。
舉個例子,你可以在這里閱讀它
該實現的這一特性不違反語言規范,因為它在未定義行為的未知區域內運行,不受任何標准要求的約束。
當然,使用某些標准庫函數的內在/內聯實現的調用不會受到重新定義的影響。
你的問題很容易引起誤解。
您看到的問題與重新實現庫函數無關。
您只是嘗試編寫不可寫內存,即存在字符串文字a
內存。
簡單來說,下面的程序在我的機器上給出了一個分段錯誤(用gcc 4.7.3
編譯,沒有標志):
#include <string.h>
int main(int argc, const char *argv[])
{
strcpy("a", "b");
return 0;
}
但是,如果您調用的是不寫不可寫內存的strcpy
(您的)版本,為什么分段會strcpy
? 僅僅因為你的功能沒有被調用。
如果使用-S
標志編譯代碼並查看編譯器為其生成的匯編代碼,則不會call
strcpy
(因為編譯器已“內聯”該調用,這是您可以進行的唯一相關調用從主要看,是對puts
的調用)。
.file "test.c"
.section .rodata
.LC0:
.string "a"
.align 8
.LC1:
.string "\nThe function ran successfully"
.text
.globl main
.type main, @function
main:
.LFB2:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movw $98, .LC0(%rip)
movq $.LC0, -8(%rbp)
movl $.LC1, %edi
call puts
movl $0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE2:
.size main, .-main
.section .rodata
.LC2:
.string "in duplicate function strcpy"
.text
.globl strcpy
.type strcpy, @function
strcpy:
.LFB3:
.cfi_startproc
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset 6, -16
movq %rsp, %rbp
.cfi_def_cfa_register 6
subq $16, %rsp
movq %rdi, -8(%rbp)
movq %rsi, -16(%rbp)
movl $.LC2, %edi
movl $0, %eax
call printf
movl $.LC0, %eax
leave
.cfi_def_cfa 7, 8
ret
.cfi_endproc
.LFE3:
.size strcpy, .-strcpy
.ident "GCC: (Ubuntu/Linaro 4.7.3-1ubuntu1) 4.7.3"
.
我覺得余浩的答案對此有很好的解釋,標准引用:
來自ISO C標准的所有庫類型,宏,變量和函數的名稱都是無條件保留的; 您的程序可能不會重新定義這些名稱。 如果您的程序明確包含定義或聲明它們的頭文件,則保留所有其他庫名。 這些限制有幾個原因:
[...]
它允許編譯器在調用這些函數時進行任何特殊的優化,而不會被用戶重新定義。
你的例子可以這樣運作:( 使用strdup )
char *strcpy(char *destination, const char *source);
int main(){
char *s = strcpy(strdup("a"), strdup("b"));
printf("\nThe function ran successfully\n");
return 0;
}
char *strcpy(char *destination, const char *source){
printf("in duplicate function strcpy");
return strdup("a");
}
輸出:
in duplicate function strcpy
The function ran successfully
解釋此規則的方法是,您不能在最終鏈接對象(可執行文件)中有多個函數定義。 因此,如果鏈接中包含的所有對象只有一個函數的定義,那么你就是好的。 記住這一點,請考慮以下方案。
我經常使用這個:
void my_strcpy(char *dest, char *src)
{
int i;
i = 0;
while (src[i])
{
dest[i] = src[i];
i++;
}
dest[i] = '\0';
}
你也可以通過修改一行來做strncpy
void my_strncpy(char *dest, char *src, int n)
{
int i;
i = 0;
while (src[i] && i < n)
{
dest[i] = src[i];
i++;
}
dest[i] = '\0';
}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.