Modifying a string inside a function by passing by reference In C - c

So, I'm facing an issue that I don't really understand. Please be kind I'm trying to learn C by myself !
I have a function that is called secureInput() that takes a pointer to a string and a size so that, when the user has to type some input I can be sure that there is no buffer overflow. Now, the thing is that I want to modify the string without copying it so instead modifying it directly by it's memory address but it just crashes when the second character in user input is assigned. See comments to see where it crashes.
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int secureInput(char **str, int size);
int main(int argc, const char * argv[]) {
char *mystring = NULL; // Declaring it a null so that I use malloc later
secureInput(&mystring, 10);
printf("%s\n", mystring);
}
int secureInput(char **str, int size)
{
*str = (char*)malloc(sizeof(char) *size); // Because **str is a null pointer, I use malloc to allocate memory.
if (*str == NULL)
return -1;
int c = 0;
unsigned int count = 0;
while((c = getchar()) != '\n' && count < size)
/* Here is where it crashes.
* But changing the bellow line as : *str[0][count++] = c;
* works as expected. Also, using a temporary pointer
* and later using it to replace *str, is also working
*/
*str[count++] = c;
*str[count] = '\0';
return 0;
}

So first off, you can pass a string as a char*, no need for char**. That is usually used for an array of strings when passed as argument. Then, if you want to use a fixed size array, a buffer, that has a constant, pre-defined size, don't use malloc. Dynamic memory allocation is always inefficient and risky, so only use it if absolutely necessary.
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <string.h>
#define BUFFER_SIZE 10
int secureInput(char *str, int size);
int main(int argc, const char * argv[]) {
char mystring[BUFFER_SIZE]; // Declaring it a null so that I use malloc later
memset(mystring, 0, BUFFER_SIZE);
secureInput(mystring, BUFFER_SIZE);
printf("%s\n", mystring);
}
int secureInput(char *str, int size) {
char c = 0;
unsigned int count = 0;
c = getchar();
while(c != '\n' && count < size - 1) {
str[count++] = c;
c = getchar();
}
str[count] = '\0';
return 0;
}
EDIT:
I can see that there is still some confusion regarding the pointer arithmetic. Here is some address printing and a small figure, I hope it helps:
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
int secureInput(char **str, int size);
int main(int argc, const char * argv[]) {
char *mystring = NULL; // Declaring it a null so that I use malloc later
secureInput(&mystring, 10);
}
int secureInput(char **str, int size) {
*str = (char*)malloc(sizeof(char) *size); // Because **str is a null pointer, I use malloc to allocate memory.
(*str)[0] = 'a';
(*str)[1] = 'b';
(*str)[2] = 'c';
(*str)[3] = 0;
printf("address of the pointer that points to a pointer that points the first char of the array : %p\n", &str);
printf("value of the pointer that points to a pointer that points to the first char of the array : %p\n", str);
printf("address of the pointer that points to the first char of the array : %p\n", &(*str));
printf("value of the pointer that points to the first char of the array : %p\n", *str);
printf("address of the first char of the array: %p\n", &(**str));
printf("address of the seconds char of the array: %p\n", &((*str)[1]));
printf("value of the first char of the array : %c\n", **str);
printf("value of the second char of the array : %c\n", *(*str + 1));
printf("value of the second char of the array : %c\n", (*str)[1]);
printf("*str[1] is the same as *(str[1]), which runs to a segmentation fault\n");
return 0;
}
The output:
address of the pointer that points to a pointer that points the first char of the array : 0x7ffce24333f8
value of the pointer that points to a pointer that points to the first char of the array : 0x7ffce2433430
address of the pointer that points to the first char of the array : 0x7ffce2433430
value of the pointer that points to the first char of the array : 0x55a91985a2a0
address of the first char of the array: 0x55a91985a2a0
address of the seconds char of the array: 0x55a91985a2a1
value of the first char of the array : a
value of the second char of the array : b
value of the second char of the array : b
*str[1] is the same as *(str[1]), which runs to a segmentation fault
0x7ffce24333f8 0x7ffce2433430
+----------------+ +----------------+ +----------------+
| 0x7ffce2433430 | -------> | 0x55a91985a2a0 | -------> | a | 0x55a91985a2a0
+----------------+ +----------------+ +----------------+
+----------------+
| b | 0x55a91985a2a1
+----------------+
+----------------+
| c | 0x55a91985a2a2
+----------------+
The point is that it matters which pointer you dereference.

At least this problem:
Off by one
str[count] = '\0'; can write outside array bounds leading to OP''s trouble. Suggest count < size --> count + 1 < size.
Entire line not always read
Reading a partial line leads to trouble.
How about reading the entire line and report results? Let calling code provide the buffer as it is of fixed size.
Distinguish between reading an empty line and end-of-file.
Handle size == 0 gracefully.
// EOF: end-of-file with no input
// EOF: input error
// 0: input, but too much
// 1: Success
int secureInput(char *str, size_t size) {
if (str == NULL) {
size = 0;
}
bool too_many = false;
size_t count = 0;
int c;
while((c = getchar()) != '\n') {
if (c == EOF) {
if (feof(stdin) && count > 0) {
break;
}
if (size > 0) {
str[0] = '\0';
}
return EOF;
}
if (count + 1 < size) {
str[count++] = c;
} else {
too_many = true;
}
}
if (count < size) {
str[count] = '\0';
}
return count < size && !too_many;
}

Related

strstr not working with needles shorter than 3 char

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* findSequence(char s[], char ch, int n){
int i;
char*ptr;
char needle[n];
char*npdr=&needle;
for(i=0;i<n;i++){needle[i]=ch;}
ptr = strstr(s,ndpr);
printf("%s",ptr);
return ptr;
}
int main()
{
char stringa[]={"ciccciopasticcio"};
char carattere='c';
char*ptr;
int n=3;
ptr=findSequence(stringa, carattere,n);
return 0;
}
This quick code, should search for a matching between a string and a needle of non set lenght, it works just fine with any n>=3 the problem is with 1 and 2 as n values.
watching the debug i noticed that the pointer npdr adds a second and third value to the sequence on his own example: n=2 needle="cc" npdr=address of needle[0] "cc#"
Do you have any ideas of why this is happening?
The standard function strstr expects two pointers to strings. But you are calling the function passing an array as the second argument that does not contain a string - a sequence of characters terminated by the zero character '\0'
char needle[n];
char*npdr=&needle;
for(i=0;i<n;i++){needle[i]=ch;}
ptr = strstr(s,ndpr);
So the code invokes undefined behavior.
Also this initialization
char*npdr=&needle;
is incorrect. The initialized pointer has the type char * while the initializing expression has the type char ( * )[n]. At least you need to write
char*npdr=needle;
You could declare the array like
char needle[n + 1];
and
needle[n] = '\0';
Pay attention to that the function should not output any message. It is the caller of the function will decide whether to output a message. Moreover this call of printf
printf("%s",ptr);
will invoke undefined behavior if the pointer ptr is equal to NULL.
The function itself should be declared the following way
char* findSequence(const char s[], char ch, size_t n);
That is the first parameter should be declared with the qualifier const because passed strings are not changed within the function. And the second parameter should have unsigned integer type size_t instead of the signed integer type int.
Also using a variable length array within the function makes the function unsafe.
The function can be defined using standard C string function strchr instead of the function strstr.
Here is a demonstration program.
#include <stdio.h>
#include <string.h>
char * findSequence( const char s[], char ch, size_t n )
{
const char *p = NULL;
if (n != 0 && ch != '\0')
{
size_t count = 0;
do
{
count = 0;
p = strchr( s, ch );
if (p)
{
s = p;
do
{
++count;
} while (*++s == ch);
}
} while (p != NULL && count != n);
}
return ( char * )p;
}
int main( void )
{
char stringa[] = { "ciccciopasticcio" };
char carattere = 'c';
size_t n = 3;
char *ptr = findSequence( stringa, carattere, n );
if (ptr != NULL)
{
puts( ptr );
}
}
The program output is
ccciopasticcio

Segmentation fault when trying to copy string with double pointer

Jus started learning about pointers and im stuck with this program outputting a segmentation fault.
Its supposed to copy the first 10 Characters of a string to the location pointed by the double pointer
using gdb ive found that **pt=*s; produces the seg fault
#include <stdio.h>
#include <stdlib.h>
void str1(char *s, char **pt);
void str1(char *s, char **pt){
for(int i=0;i<10;i++){
**pt=*s;
pt++;
s++;
}
}
int main (void) {
char str[30] = "223This is test";
char *ptr;
str1(str, &ptr);
printf("%s", ptr);
return 0;
}
First of all ptr is not initialized, you can't really use it until you reserve space for it or store a valid memory address in it, i.e. make it point to some valid variable.
char *ptr = malloc(11);
Then you need to increment it properly in the function:
(*pt)++;
Once the copy is completed you need to null terminate the char array so it can be treatead as a string, aka a null terminated char array.
**pt = '\0';
Now as ptr was passed as a pointer to pointer, the increment is known by the caller, main in this case, so when you try to print it, it prints nothing because it's pointing to the end of the char array, we need to bring it back to the beggining.
*pt -= 10;
Corrected code with comments taking yours as base:
Live demo
#include <stdio.h>
#include <stdlib.h>
#define SIZE 10
void str1(char *s, char **pt) {
for (int i = 0; i < SIZE; i++) {
**pt = *s;
(*pt)++; //properly increment pt
s++;
}
**pt = '\0'; //null terminate copied string
//since ptr was passed as **, the increment is known by the caller
//now ptr will be pointing to the end of the string
//we have to bring it back to the beginning
*pt -= SIZE;
}
int main(void) {
char str[] = "223This is test";
char *ptr = malloc(SIZE + 1); //space for 10 character + null-terminator
//check for allocation errors
if(ptr == NULL){
perror("malloc");
return EXIT_FAILURE;
}
str1(str, &ptr);
printf("%s", ptr);
free(ptr);
return EXIT_SUCCESS;
}
You probably want this:
#include <stdio.h>
#include <stdlib.h>
void str1(char* s, char** pt) {
char *p = malloc(100); // allocate memory for destination
*pt = p; // store it for the caller
for (int i = 0; i < 10; i++) {
*p = *s;
p++;
s++;
}
*p = 0; // put string terminator, otherwise printf won't work correctly
}
int main(void) {
char str[30] = "223This is test";
char *ptr; // for now p points nowhere
str1(str, &ptr); // now p points to the memory allocated in str1
printf("%s", ptr);
free(ptr); // free memory for completeness
return 0;
}

Passing the address of a dereferenced pointer to a string offset

Assuming there is a function like this
int foo (char** str, int x)
{
char* p = *str + x;
foo2(&p); // declared as int foo2 (char** );
}
(oversimplified of course, the real function is recursive and much more complicated)
I've tried to do this:
int foo (char** str, int x)
{
foo2(&(*str + x));
}
But the compiler failed with error:
error: lvalue required as unary '&' operand
Why did the compiler shoot out with this error and how do I pass the pointer to a pointer to string x-byte(s) forwards, without declaring a variable and use its own address?
EDIT
Seems like there is some misunderstanding so I will post a complete simulation of what I want to achieve.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* string = "This is a sample string.";
char* ptr;
int randomizer;
int receive_string (char* buffer, int size) // recv
{
int i = 0;
if(ptr == NULL)
ptr = string;
for(i = 0; *ptr != '\0' && i < size; ptr++)
{
if(randomizer == 2)
{
randomizer++;
break;
}
buffer[i] = *ptr;
i++;
randomizer++;
}
if(*ptr == '\0')
{
buffer[i] = *ptr;
i++;
}
return i;
}
int read_string (char* *buffer, int size, int alloc)
{
int bytes = 0;
printf("Reading string..\n");
if(*buffer == NULL && alloc == 1)
{
printf("Allocating buffer..\n");
*buffer = calloc(size, sizeof(char));
}
bytes = receive_string(*buffer, size);
if(bytes == (-1))
{
return(-1);
}
if(bytes == 0)
{
return 0;
}
if(bytes < size)
{
char* p = *buffer + bytes;
//int temp = read_string(&p, size - bytes, 0); // works
//int temp = read_string(&(char *){&(*buffer)[bytes]}, size - bytes, 0); // works
int temp = read_string(buffer + bytes, size - bytes, 0); // doesn't work
if(temp > 0)
bytes += temp;
else return bytes;
}
return bytes;
}
int main()
{
char* buffer = NULL;
int bytes = read_string(&buffer, strlen(string) + 1, 1);
printf("[%u][%s]\n", bytes, buffer);
if(buffer)
free(buffer);
return 0;
}
The randomizer is the dumbest quickie to "simulate" a recv() that can not receive all bytes. This implementation simulates recv() but instead of reading from a socket queue it reads from a global string.
(*str + x) is not an lvalue as it is a temporay value that does not have an address so you cannot take its address with &. Even if the compiler stored the value in a temporary variable in RAM so its address could be taken how would you reference its value afterwards if foo2() modified the contents of the temporay variable.
Therefore you need to store the value in a temporary variable yourself.
if you want to pass the pointer to pointer to the particular char
foo2(&(char *){&(*str)[x]});
or
I think the following code is what you are trying to do. For kicks, I made it recursive and tested it with the alphabet for a string. Variables cnt and lmt need to be global. It will show a shrinking string if you run it. Just be sure to keep p and lmt small enough to not overflow the string.
void foo(char *s, int p) {
cnt++;
printf("%s\n", s);
if(cnt != lmt) foo(&s[p], p);
}

how can i use pointer to move inside of this array (function) instead of array subscribting

my question is at the generate acronym function, how can i make this function work in a pointer arithmetic way instead of array subscripting.
without messing up with the structures itself, the prof prhobited array subscribting so i have to do it with pointer arithmetic instead, anyone can land a hand?
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <ctype.h>
#define B 2
#define N 8
typedef struct {
int course_id;
int course_quota;
char course_name[50];
char course_code[6];
char course_acronym[N];
}course_t;
void generate_course_code(char *course_code, int course_id);
void generate_course_acronym(char *, char *);
void display();
course_t courses[B];
int main() {
int i;
for(i = 0; i < B; i++) {
printf("Enter the course name: ");
fgets(courses[i].course_name, sizeof(courses[i].course_name), stdin);
generate_course_acronym(courses[i].course_name, courses[i].course_acronym);
printf("Enter the course Quota: ");
scanf("%d", &courses[i].course_quota);
while ('\n' != getchar())
{
}
courses[i].course_id = i;
generate_course_code(courses[i].course_code, courses[i].course_id);
}
display();
return 0;
}
void generate_course_code(char *course_code, int course_id) {
char str[6];
course_id++;
strcpy(course_code, "CSE");
if (course_id < 10) {
sprintf(str, "0%d", course_id);
}
else
sprintf(str, "%d", course_id);
strcat(course_code, str);
}
void generate_course_acronym(char *course_name, char *course_acronym) {
int j = 0;
char *p = course_name;
for (course_acronym[j++] = toupper(*p); *p != '\0'; p++)
if (*p == ' ') course_acronym[j++] = toupper(*(++p));
course_acronym[j] = '\0';
}
void display() {
int x;
for (x = 0; x < B; x++) {
printf("%d. %s - %s (%s) - %d \n", ++courses[x].course_id, courses[x].course_code, courses[x].course_name, courses[x].course_acronym, courses[x].course_quota);
}
}
Because the function arguments are provided as pointers, they can be used as is to achieve your goal using pointer arithmetic as shown below:
(explanations in comments).
void generate_course_acronym(char *course_name, char *course_acronym)
{
*course_acronym = (char)toupper(*course_name); //initialize acronym to capitalized form of first character
while(*course_name) //test for end of string (NULL)
{
if(*course_name == ' ') // detect spaces between strings
{
course_acronym++; // advance acronym pointer
course_name++; // advance source pointer beyond space.
*course_acronym = (char)toupper(*course_name); // assign next acronym character
}
course_name++; // advance source to next address location
}
course_acronym++; // advance pointer to one location beyond end of acronym
*course_acronym = '\0'; // NULL terminate, making acronym a new string
}
Note: The calling function must pass enough space in course_acronym to accommodate 1 byte for each word in course_name, + 1 extra byte. For example, if course_name is defined as:
char course_name[]={"This is a fine programming class"};
then course_acronym must be defined with space for at least 7 bytes. (count of words + 1 for NULL termination)

Dynamic and static char array

Here are the codes of a program:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * cloning(char * q){
char s[strlen(q)];
int i;
for(i = 0; i < strlen(q); i++)
s[i] = q[i];
return s;
}
int main(){
char q[] = "hello";
char *s = cloning(q);
return 0;
}
After the compilation a warning appears, so I changed the returned value like this:
char *b = s;
return b;
In this way the warning can be solved. However I found that inside the function cloning(), sizeof(s) is 5, but strlen(s) is 7. And if I change char s[strlen(q)] simply to char s[5], the output is still incorrect. Can anybody explain this problem to me? Thank you very much.
char s[strlen(q)] is a local variable, and hence when you return its address, it results in undefined behaviour. Thus either you could use strdup() or malloc() to dynamically allocate the array, thus ensuring that the array s is available on the heap when you return from the function. The returned array would need to be free()-ed as well, else it would have a memory leak :)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char * cloning(char * q){
char *s = malloc(strlen(q)+1);
// if you write char s[strlen(q)], it is defined locally, and thus on return gives an undefined behaviour
int i;
for(i = 0; i < strlen(q)+1; i++)
s[i] = q[i];
return s;
}
int main(){
char q[] = "hello";
char *s = cloning(q);
free(s);
return 0;
}
char s[strlen(q)];
is a variable-length array. Like a malloc'ed buffer its size is determined at runtime. Unlike a malloc'ed buffer, it ceases to exist when the function returns.
multiple issues with this code:
char * cloning(char * q){
char s[strlen(q)]; // s has strlen size but needs strlen + 1 to hold \0
int i;
for(i = 0; i < strlen(q); i++) // should copy the \0 otherwise q is not a valid string
s[i] = q[i];
return s;// returns the address of a local var == undef. behavior
}
if you want to clone a string just do strdup()
char* cloning(char* q)
{
return strdup(q);
}
or the equivalent
char * cloning(char * q)
{
char* s = malloc(strlen(q)+1);
int i;
for(i = 0; i < strlen(q)+1; i++)
s[i] = q[i];
return s;
}
The proper way to do this with standard C, no matter version of the C standard, is this:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char* cloning (const char* str)
{
char* clone;
size_t size = strlen(str) + 1;
clone = malloc(size);
if(clone == NULL)
{
return NULL;
}
memcpy(clone, str, size);
return clone;
}
int main(){
char original[] = "hello";
char* clone = cloning(original);
if(clone == NULL)
{
puts("Heap allocation failed.");
return 0;
}
puts(clone);
free(clone);
return 0;
}
Dynamic arrays in C are declared using Malloc and Calloc. Try googling it.
Eg:
char *test;
test = (char *)malloc(sizeof(char)*Multiply_By);
In C,static array is in stack,after function return,it's been destoryed. and string with char has a '\0' end. But strlen don't include it. For example.char q[] = "hello"; strlen(q) = 5,but the real size is 6
If you want to copy a string, the last '\0' must be added at the end.or using
char *s = malloc(sizeof(q)); ...; for(i = 0; i < sizeof(q); i++)
s[i] = q[i];
you also need to free it after using.Maybe become a mem leak.
Hope this can help u.

Resources