การสร้างอักษรตัวพิมพ์ใหญ่โดยไม่มีตัวชี้

ฉันกำลังพยายามเขียนฟังก์ชันตัวพิมพ์ใหญ่ที่แปลงอักขระตัวพิมพ์เล็กทั้งหมดในสตริงให้เป็นตัวพิมพ์ใหญ่ที่เทียบเท่ากัน

อย่างไรก็ตาม ฉันได้รับข้อผิดพลาด Bus 10 ในรหัสของฉัน ฉันรู้ว่าไม่สามารถแก้ไขตัวอักษรสตริงในภาษา C; ดังนั้นฉันไม่แน่ใจว่านี่เป็นแนวทางที่ถูกต้องหรือไม่

รหัสของฉันอยู่ด้านล่าง:

#include <stdio.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>

int uppercase(char source[])
{
 int i;

 for(i=0; i<=strlen(source); ++i)
    if (source[i]>= 'a' && source[i]<= 'z')
        source[i]= source[i]-'a' +'A';
    else 
        source[i]=source[i];
}

int main(){
    uppercase("cold");

    return 0;
}

ตามหลักการแล้วฟังก์ชันนี้ควรส่งคืน COLD ฉันคิดว่าข้อผิดพลาดอยู่ในคำสั่ง if ทั้งหมดของฉัน


person Rohit Tigga    schedule 21.12.2013    source แหล่งที่มา
comment
คุณได้ตอบคำถามของคุณเองแล้ว และคุณหมายถึงอะไรโดยการกลับมา?   -  person Kerrek SB    schedule 22.12.2013
comment
และอะไรคือประเด็นของ source[i] = source[i]??   -  person Kerrek SB    schedule 22.12.2013
comment
ฉันขอโทษที่ฉันควรจะชัดเจนกว่านี้ ฉันไม่ทราบวิธีการแก้ไขข้อผิดพลาดในกรณีนี้ ประเด็นก็คือต้องไม่มีการเปลี่ยนแปลง ฉันเดาว่ามันซ้ำซ้อน?   -  person Rohit Tigga    schedule 22.12.2013
comment
ฉันเดาว่าคุณควรรวมฟังก์ชัน/เมธอดตัวพิมพ์ใหญ่ () ด้วย   -  person Doro    schedule 22.12.2013
comment
ขอบคุณคุณชูซ์ แต่คุณสามารถให้คำอธิบายได้ไหม?   -  person Rohit Tigga    schedule 22.12.2013
comment
@XiJiaopin คนอื่น ๆ ก็มีให้อย่างดี   -  person chux - Reinstate Monica    schedule 22.12.2013


คำตอบ (2)


เหตุผลที่คุณได้รับข้อขัดข้องก็คือโค้ดของคุณแก้ไขสตริงลิเทอรัล อักขระภายในตัวอักษรสตริงจะถูกวางไว้ในพื้นที่หน่วยความจำที่ได้รับการป้องกัน ดังนั้นจึงไม่สามารถเปลี่ยนแปลงได้: เป็นพฤติกรรมที่ไม่ได้กำหนดไว้

แทนที่สิ่งนี้

uppercase("cold");

ด้วยสิ่งนี้:

char cold[] = "cold";
uppercase(cold);

ตอนนี้อักขระของสตริงถูกวางไว้ในพื้นที่หน่วยความจำที่แก้ไขได้ ช่วยให้คุณสามารถทำการเปลี่ยนแปลงได้ตามต้องการ

person Sergey Kalinichenko    schedule 21.12.2013
comment
@XiJiaopin: ยินดีต้อนรับสู่ SO! สถานะของคุณแสดงว่าคุณยังไม่ได้อ่านหน้า เกี่ยวกับ โปรดดำเนินการเร็วๆ นี้ สำหรับตอนนี้ โปรดอ่าน มีคนตอบคำถามของฉัน! จะทำอย่างไรตอนนี้? และพิจารณายอมรับหนึ่งในคำตอบสำหรับคำถามของคุณ - person Jongware; 22.12.2013

คุณทำงานกับพอยน์เตอร์โดยที่ไม่รู้ตัวเลย

ในนิยามฟังก์ชันของคุณ

int uppercase(char source[])

char source[] ได้รับการพิจารณาโดยคอมไพเลอร์ว่าเป็นตัวชี้ไปยังถ่าน (char *source)

ดังนั้นเมื่อส่งสตริงตามตัวอักษรไปที่ uppercase() คุณเพิ่งส่งที่อยู่นั้นไป จากนั้นในฟังก์ชันของคุณ คุณพยายามแก้ไขมันซึ่งนำไปสู่พฤติกรรมที่ไม่ได้กำหนดไว้

นอกจากนี้คุณไม่สามารถส่งคืนอาร์เรย์ทั้งหมดได้ ดังนั้นคุณเพียงแค่ส่งคืนตัวชี้ไปที่อาร์เรย์นั้น

char *uppercase(char source[])
{
     int i;
     size_t len = strlen(source);
     char *tmp;
     tmp = malloc(len+1);
     if (tmp!=NULL){
         memcpy(tmp, source, len+1);
         for(i=0; i<len; ++i){
            if (tmp[i]>= 'a' && tmp[i]<= 'z'){
                tmp[i]= tmp[i]-'a' +'A';
            }
        }
    }
    return tmp;
}

แล้ว:

int main(){
    char *str = uppercase("cold");
    printf("%s", str);
    free(str);

    return 0;
}

คุณกรอกโค้ด: http://ideone.com/BJHDIF

person rullof    schedule 21.12.2013
comment
คุณต้องจัดสรรพื้นที่สำหรับตัวยุติที่เป็นโมฆะและควรเพิ่ม free(tmp); ใน main จริงๆ - person simonc; 22.12.2013
comment
strlen ไม่มีจุดสิ้นสุดที่เป็นโมฆะใช่ไหม เหตุใดจึงต้องปล่อย tmp สิ่งนี้จะนำไปสู่การสูญเสียเนื่องจากฟังก์ชันกำลังส่งคืน - person rullof; 22.12.2013
comment
ไม่ strlen ไม่รวมตัวสิ้นสุด คุณไม่สามารถโทร free เพื่อรับผลตอบแทนที่จัดสรรแบบไดนามิกจาก uppercase จนกว่าคุณจะดำเนินการเสร็จ แต่คุณควรปล่อยมันให้ว่างในบางจุด ข้อเสนอแนะของคุณทำให้หน่วยความจำนี้รั่วไหล ไม่ใช่เรื่องใหญ่ในโปรแกรมเล็กๆ นี้ แต่เป็นคำแนะนำที่แย่มากสำหรับการเขียนโปรแกรมโดยทั่วไป - person simonc; 22.12.2013
comment
คุณลืมจุดสิ้นสุดที่เป็นโมฆะ :) - person rullof; 22.12.2013
comment
ฉันไม่คิดอย่างนั้น - malloc(len+1) จัดสรรพื้นที่ให้และ strcpy คัดลอกมัน ฉันพลาดไปว่าการวนซ้ำเสร็จสิ้นหนึ่งอักขระก่อนกำหนด - ตอนนี้ได้รับการแก้ไขแล้ว - person simonc; 22.12.2013
comment
เนื่องจากคุณมีความยาวอยู่แล้วโดยใช้ strcpy ถือเป็น overkill: ใช้ memcpy(tmp, source, len + 1) (ฉันจะคัดลอกสตริงในรอบแทน) ประเภทการส่งคืนของ strlen() คือ size_t ไม่ใช่ int เช่นเดียวกับประเภทอาร์กิวเมนต์ malloc คุณยังทำ len = strlen(tmp); ขณะที่คุณหมายถึง source ที่นี่ ไม่ใช่ tmp และคุณควรตรวจสอบว่า malloc ส่งคืน NULL หรือไม่ - person ZyX; 22.12.2013
comment
@ZyX จะตรวจสอบได้อย่างไรว่า malloc ส่งคืน NULL และทำไม memcpy ถึงดีกว่า strcpy - person rullof; 22.12.2013
comment
@rullof 1. ?! จะตรวจสอบได้อย่างไรว่าตัวชี้เป็นโมฆะ? ใช้ if (tmp == NULL) 2. strcpy ทำงานกับสตริงที่ไม่ทราบความยาว เช่น. ต้องดำเนินการกับสตริงในลักษณะที่จะไม่เข้าถึงหน่วยความจำหลังจาก NUL ไบต์ memcpy คัดลอกก้อนหน่วยความจำต่อเนื่องพร้อมตำแหน่งเริ่มต้นและความยาวที่กำหนด ไม่จำเป็นต้องตรวจสอบว่ากลุ่มที่กำหนดมีไบต์เฉพาะเจาะจงหรือไม่ และไม่จำเป็นต้องประมวลผลหน่วยความจำจากตำแหน่งเฉพาะ (เช่น จุดเริ่มต้นของกลุ่ม) และในทิศทางที่กำหนด (เช่น ตั้งแต่ต้นจนจบ) ซึ่งหมายความว่าสามารถเพิ่มประสิทธิภาพได้มากขึ้นในการใช้งาน memcpy . - person ZyX; 22.12.2013
comment
หมายเหตุเกี่ยวกับ malloc: มันจะคืนค่า NULL หากไม่มีหน่วยความจำ ในกรณีนี้ แอปของคุณอาจถูกคาดหวังให้ลองเพิ่มหน่วยความจำบางส่วน แต่ส่วนใหญ่แล้วจะเกิดข้อผิดพลาดโดยบ่นว่าไม่มีหน่วยความจำ (หากคุณไม่เพิ่มการตรวจสอบนี้ มันจะเกิดข้อผิดพลาดโดยไม่มีข้อความที่เข้าใจง่าย เนื่องจากคุณพยายามเขียนไปยังที่อยู่ 0x0 (NULL )). - person ZyX; 22.12.2013
comment
!==? มันไม่ใช่จาวาสคริปต์ คุณยังสามารถปล่อยสาขา else ได้: ถ้า tmp == NULL ไม่มีความแตกต่างระหว่าง return NULL และ return tmp เลย และ main() ไม่สนใจกรณีที่ฟังก์ชัน uppercase() ส่งคืน NULL - person ZyX; 22.12.2013
comment
Uggg พลาดอีกแล้ว ฉันควรจะไปสุวิสัย - person rullof; 22.12.2013
comment
สิ่งสำคัญที่ฉันไม่ชอบในภาษา C คือบางครั้งโค้ดมากกว่าครึ่งหนึ่งกำลังจัดการข้อผิดพลาด การจัดการข้อผิดพลาดมากกว่าครึ่งหนึ่งคือการส่งข้อผิดพลาดจากผู้โทรไปยังผู้โทร - person ZyX; 22.12.2013