Programing

함수에서 C 문자열 반환

crosscheck 2020. 8. 28. 06:51
반응형

함수에서 C 문자열 반환


함수에서 C 문자열을 반환하려고하지만 작동하지 않습니다. 다음은 내 코드입니다.

char myFunction()
{
    return "My String";
}

기본적으로 다음과 같이 부릅니다.

int main()
{
  printf("%s",myFunction());
}

나는 또한 다른 방법을 시도했지만 myFunction작동하지 않습니다. 예 :

char myFunction()
{
  char array[] = "my string";
  return array;
}

참고 : 포인터를 사용할 수 없습니다!

이 문제에 대한 약간의 배경 : 예를 들어 어느 달인지 알아내는 기능이 있습니다. 1이면 1 월 등을 반환합니다.

따라서 인쇄 할 때 이렇게됩니다. printf("Month: %s",calculateMonth(month));. 이제 문제는 calculateMonth함수 에서 해당 문자열을 반환하는 방법 입니다.


함수 서명은 다음과 같아야합니다.

const char * myFunction()
{
    return "My String";
}

편집하다:

배경:

이 게시물이 나온 지 몇 년이 지났으며 C & C ++의 기본이기 때문에 투표가 될 것이라고 생각하지 못했습니다. 그럼에도 불구하고 조금 더 논의가 필요합니다.

C (& C ++)에서 문자열은 0 바이트로 끝나는 바이트 배열 일뿐입니다. 따라서 "문자열 0"이라는 용어는이 특정 문자열의 특징을 나타내는 데 사용됩니다. 다른 종류의 문자열이 있지만 C (& C ++)에서이 풍미는 본질적으로 언어 자체에 의해 이해됩니다. 다른 언어 (Java, Pascal 등)는 "my string"을 이해하기 위해 다른 방법론을 사용합니다.

Windows API (C ++에 있음)를 사용하는 경우 "LPCSTR lpszName"과 같은 매우 정기적으로 함수 매개 변수가 표시됩니다. 'sz'부분은 '문자열 0'이라는 개념을 나타냅니다. 즉, 널 (/ 0) 종결자가있는 바이트 배열입니다.

설명:

이 '인트로'를 위해 '바이트'와 '문자'라는 단어를 같은 의미로 사용합니다. 이렇게 배우는 것이 더 쉽기 때문입니다. 국제 문자를 처리하는 데 사용되는 다른 방법 (와이드 문자 및 멀티 바이트 문자 시스템 --mbcs)이 있다는 점에 유의하십시오. UTF-8은 mbcs의 예입니다. 소개를 위해이 모든 것을 조용히 '건너 뛰어'.

기억:

이것이 의미하는 것은 "my string"과 같은 문자열이 실제로 9 + 1 (= 10!) 바이트를 사용한다는 것입니다. 이것은 마지막으로 문자열을 동적으로 할당 할 때를 아는 것이 중요합니다. 따라서이 '종료 0'이 없으면 문자열이 없습니다. 메모리에는 문자 배열 (버퍼라고도 함)이 있습니다.

데이터 수명 :

이 방법으로 함수 사용 :

const char * myFunction()
{
    return "My String";
}
int main() 
{
    const char* szSomeString = myFunction(); // fraught with problems
    printf("%s", szSomeString);
}

... 일반적으로 임의의 처리되지 않은 예외 / 세그먼트 오류 등, 특히 '길 아래'로 착지합니다.

요컨대, 내 대답은 맞지만 10 번 중 9 번은 그런 식으로 사용하면 충돌하는 프로그램으로 끝날 것입니다. 특히 그렇게하는 것이 '좋은 습관'이라고 생각하는 경우에 그렇습니다. 요약하자면 일반적으로 그렇지 않습니다.

예를 들어, 미래의 언젠가를 상상해보십시오. 이제 문자열을 어떤 방식 으로든 조작해야합니다. 일반적으로 코더는 '쉬운 길을 택'하고 다음과 같은 코드를 작성합니다.

const char * myFunction(const char* name)
{
    char szBuffer[255];
    snprintf(szBuffer, sizeof(szBuffer), "Hi %s", name);
    return szBuffer;
}

즉, 컴파일러 szBufferprintf()in main()이 호출 될 때 사용 된 메모리를 해제했기 때문에 프로그램이 중단됩니다 . (컴파일러는 이러한 문제에 대해 미리 경고해야합니다.)

너무 쉽게 바 프지 않는 문자열을 반환하는 두 가지 방법이 있습니다.

  1. 잠시 동안 살아있는 버퍼 (정적 또는 동적 할당)를 반환합니다. C ++에서 '도우미 클래스'(예 std::string:)를 사용하여 데이터의 수명을 처리합니다 (함수의 반환 값을 변경해야 함).
  2. 정보로 채워지는 함수에 버퍼를 전달합니다.

C에서 포인터를 사용하지 않고 문자열을 사용하는 것은 불가능하다는 점에 유의하십시오. 내가 보여준 것처럼 그것들은 동의어입니다. 템플릿 클래스가있는 C ++에서도 항상 백그라운드에서 사용되는 버퍼 (예 : 포인터)가 있습니다.

따라서 (현재 수정 된 질문)에 더 잘 대답하십시오. (제공 할 수있는 다양한 '기타 답변'이있을 것임이 분명합니다.)

더 안전한 답변 :

예 1. 정적으로 할당 된 문자열 사용 :

const char* calculateMonth(int month) 
{
    static char* months[] = {"Jan", "Feb", "Mar" .... }; 
    static char badFood[] = "Unknown";
    if (month<1 || month>12) 
        return badFood; // choose whatever is appropriate for bad input. Crashing is never appropriate however.
    else
        return months[month-1];
}
int main()
{
    printf("%s", calculateMonth(2)); // prints "Feb"
}

여기서 '정적'이하는 일은 (많은 프로그래머가 이러한 '할당'유형을 좋아하지 않음) 문자열이 프로그램의 데이터 세그먼트에 들어가는 것입니다. 즉, 영구적으로 할당됩니다.

C ++로 이동하면 비슷한 전략을 사용하게됩니다.

class Foo 
{
    char _someData[12];
public:
    const char* someFunction() const
    { // the final 'const' is to let the compiler know that nothing is changed in the class when this function is called.
        return _someData;
    }   
}

...하지만 std::string다른 사람과 공유 할 라이브러리의 일부가 아닌 자신의 용도로 코드를 작성하는 경우 와 같은 도우미 클래스를 사용하는 것이 더 쉬울 것입니다 .

예 2. 호출자 정의 버퍼 사용 :

이것은 문자열을 전달하는 더 '거울 증거'방법입니다. 반환 된 데이터는 발신자에 의해 조작되지 않습니다. 즉, 예를 들어 1은 발신자에 의해 쉽게 남용 될 수 있으며 응용 프로그램 오류에 노출 될 수 있습니다. 이렇게하면 훨씬 더 안전합니다 (더 많은 코드 줄을 사용하지만).

void calculateMonth(int month, char* pszMonth, int buffersize) 
{
    const char* months[] = {"Jan", "Feb", "Mar" .... }; // allocated dynamically during the function call. (Can be inefficient with a bad compiler)
    if (!pszMonth || buffersize<1) 
        return; // bad input. Let junk deal with junk data.
    if (month<1 || month>12)
    {
        *pszMonth = '\0'; // return an 'empty' string 
        // OR: strncpy(pszMonth, "Bad Month", buffersize-1);
    }
    else
    {
        strncpy(pszMonth, months[month-1], buffersize-1);
    }
    pszMonth[buffersize-1] = '\0'; // ensure a valid terminating zero! Many people forget this!
}

int main()
{
    char month[16]; // 16 bytes allocated here on the stack.
    calculateMonth(3, month, sizeof(month));
    printf("%s", month); // prints "Mar"
}

There are lots of reasons why the 2nd method is better, particularly if you're writing a library to be used by others (you don't need to lock into a particular allocation/deallocation scheme, 3rd parties can't break your code, you don't need to link to a specific memory management library), but like all code, it's up to you on what you like best. For that reason, most people opt for eg 1 until they've been burnt so many times that they refuse to write it that way anymore ;)

disclaimer:

I retired several years back and my C is a bit rusty now. This demo code should all compile properly with C (it is ok for any C++ compiler though).


A C string is defined as a pointer to an array of characters.

If you cannot have pointers, by definition you cannot have strings.


Note this new function:

const char* myFunction()
{
        static char array[] = "my string";
        return array;
}

I defined "array" as static, otherwise when the function end, the variable (and the pointer you are returning) gets out of scope. Since that memory is allocated on the stack, it will get corrupted. The downside of this implementation is that the code is not re-entrant and not thread-safe.

Another alternative would be to use malloc to allocate the string in the heap, and then free on the correct locations of your code. This code will be re-entract and thread safe.

EDIT:

As noted in the comment, this is a very bad practice, since an attacker can then inject code to your app (he needs to open the code using gdb, then make a breakpoint and modify the value of a returned variable to overflow and fun just gets started).

If is much more recommended to let the caller handle about memory allocations. See this new example:

char* myFunction( char* output_str, size_t max_len )
{
   const char *str = "my string";
   size_t l = strlen(str);
   if (l+1 > max_len) {
      return NULL;
   }
   strcpy(str, str, l);
   return input;
}

Note that the only content which can be modified is the one that the user. Another side effect - this code is now thread safe, at least from the library point of view. The programmer calling this method should verify that the memory section used is thread safe.


Your problem is with the return type of the function - it must be:

char *myFunction()

...and then your original formulation will work.

Note that you cannot have C strings without pointers being involved, somewhere along the line.

Also: Turn up your compiler warnings, it should have warned you about that return line converting a char * to char without an explicit cast.


Based on your newly-added backstory with the question, why not just return an integer from 1 to 12 for the month, and let the main() function use a switch statement or if-else ladder to decide what to print? It's certainly not the best way to go - char* would be - but in the context of a class like this I imagine it's probably the most elegant.


You can create the array in the caller, which is the main function, and pass the array to the callee which is your myFunction(). Thus myFunction can fill the string into the array. However you need to declare myFunction() as

char* myFunction(char * buf, int buf_len){
  strncpy(buf, "my string", buf_len);
  return buf;
}

and in main function, myFunction should be called in this way

char array[51];
memset(array,0,51);/*all bytes are set to '\0'*/
printf("%s", myFunction(array,50));/*buf_len arguement is 50 not 51. This is to make sure the string in buf is always null-terminated(array[50] is always '\0')*/

However pointer is still used.


Your function return type is a single char. You should return a pointer to the first element of the character array. If you can't use pointers, then you are screwed. :(


Or how about this one:

void print_month(int month)
{
    switch (month)
    {
        case 0:
            printf("january");
            break;
        case 1:
            printf("february");
            break;
        ...etc...
    }
}

And call that with the month you compute somewhere else.

Regards,

Sebastiaan


A char is only a single one-byte character. It can't store the string of characters, nor is it a pointer (which you apparently cannot have). Therefore you cannot solve your problem without using pointers (which char[] is syntactic sugar for).


Well in your code you are trying to return a String (In C which is nothing but null terminated array of chars) but return type of your function is char which is causing all the trouble for you. Instead you should write it this way:

const char* myFunction()
{

    return "My String";

}

And it's always good to qualify your type with const while assigning literals in C to pointers as literals in C aren't modifiable.


If you really can't use pointers, do something like this:

char get_string_char(int index)
{
    static char array[] = "my string";
    return array[index];
}

int main()
{
    for (int i = 0; i < 9; ++i)
        printf("%c", get_string_char(i));
    printf("\n");
    return 0;
}

The magic number 9 is awful, this is not an example of good programming. But you get the point. Note that pointers and arrays are the same thing (kinda) so this is a bit cheating.

Hope this helps!


Your function prototype states your function will return a char. Thus, you can't return a string in your function.


char* myFunction()
{
    return "My String";
}

In C string literals are arrays with static constant memory class, so returning a pointer to this array is safe. More details here: "life-time" of string literal in C

참고URL : https://stackoverflow.com/questions/1496313/returning-c-string-from-a-function

반응형