problem in calling a function converting string to lowercase using c [closed] - c

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
Below is the c code to kill a window in a versatile way.
When I call it in this way, it shows the message and then got stuck.
killWindowsVersatile.exe key ci ever 2 1000 "Super"
...closing window with argument:key,ci,ever,2,1000,Super,
name_key = 2;
cs_ci = 2;
ever_once = 1;
delay for 2 seconds start...
remaining 2 seconds ...
remaining 1 seconds ...
remaining 0 seconds ...
entering ever mode
Below is the code of the file:
It kills window either by name or a key word, and can delay for seconds, and can be case sensitive or insensitive. It can also loop the command by itself in a interval time of spacing.
There may be something wrong in the to_lowercase function.
#include <stdio.h>
#include <stdlib.h>
#define WINVER 0x0600
#define _WIN32_IE 0x0500
#include <windows.h>
#include <stdint.h>
#include <ctype.h>
int name_key,cs_ci,ever_once,delay;
//the order of function is important! you cannot call something before defined !
const char * to_lowercase(char* str){
int len = strlen(str);
char * newStr[len];
for(int i = 0; str[i]; i++){
newStr[i] = tolower(str[i]);
}
return newStr;
}
BOOL CALLBACK WindowFoundCB(HWND hwnd, char* param){
char *key = (char*) param;
char title[256]; // This way, you have allocated 256 bytes for the Window name.
GetWindowText(hwnd, title, 256);
HWND me = GetConsoleWindow();
if (IsWindowVisible(hwnd) && hwnd != me){
if(cs_ci==1){
char *p = strstr(title,key);
if (p!= NULL){
SendMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);
printf("==== close a window by key [%s]: <%s>\n",key,title);
}
}else if(cs_ci==2){
char title_lower[256];
char key_lower[256];
strcpy(title_lower,to_lowercase(title));
strcpy(key_lower,to_lowercase(key));
//char *title_lower = to_lowercase(title);
//char *key_lower = to_lowercase(key);
char *p = strstr(title_lower,key_lower);
if (p!= NULL){
SendMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);
printf("==== close a window by key [%s]: <%s>\n",key,title);
}
}
}
return TRUE;
}
int dealwithonce(int name_key,int cs_ci,char *argv[],int argc){
for(int i = 6; i < argc; i++){
if(name_key == 1){
HWND hwnd = FindWindow(NULL, argv[i]);
if(hwnd){
char strIte[256]; // This way, you have allocated 256 bytes for the Window name.
GetWindowText(hwnd, strIte, 256);
if( (cs_ci==2) || ((cs_ci==1)&& !strcmp(strIte,argv[i])) ){
SendMessage(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0);
printf("==== close a window by name [%s]\n",argv[i]);
}
}
}else if(name_key == 2){
char* aStr = argv[i];
EnumWindows(WindowFoundCB,aStr);
}
}
}
int main(int argc, char *argv[])
{
// argumrnt check
printf("...closing window with argument:");
for(int i = 1; i < argc; i++){
printf("%s,", argv[i]);
}
printf("\n");
// argument parse
if(argc < 7){
printf("%s\n","you must input 5 arguments at least!");
printf("%s\n","arg1: [name] or [key]");
printf("%s\n","arg2: [cs] for case-sensitive or [ci] for case-insensitive");
printf("%s\n","arg3: [ever] for forever or [once] for once");
printf("%s\n","arg4: the seconds for delay before start., 0,1,2...");
printf("%s\n","arg5: the milliseconds of Cycle for ever mode., 1,2,500,1000,...");
printf("%s\n","arg6+: the assembly of any strings");
return 0;
};
if(!strcmp(argv[1],"name")){
printf("name_key = 1;\n");
name_key = 1;
}else if(!strcmp(argv[1],"key")){
printf("name_key = 2;\n");
name_key = 2;
}
if (!strcmp(argv[2],"cs")){
printf("cs_ci = 1;\n");
cs_ci = 1;
}else if(!strcmp(argv[2],"ci")){
printf("cs_ci = 2;\n");
cs_ci = 2;
}
if (!strcmp(argv[3],"ever")){
printf("ever_once = 1;\n");
ever_once = 1;
}else if(!strcmp(argv[3],"once")){
printf("ever_once = 2;\n");
ever_once = 2;
}
// delay
delay = atoi(argv[4]);
if(delay > 0){
printf("delay for %d seconds start...\n",delay);
for(int i=delay;i>=0;i--){
printf("remaining %d seconds ...\n",i);
Sleep(1000);
}
}
//cycle
int interval = atoi(argv[5]);
// ever or not
if(ever_once == 1){
printf("entering ever mode\n");
while(1){
dealwithonce(name_key,cs_ci,argv,argc);
Sleep(interval);
}
}else if(ever_once == 2){
printf("entering once mode\n");
dealwithonce(name_key,cs_ci,argv,argc);
}
return 0;
}

strcmp returns a value that represents how argument 1 compares to argument 2 alphabetically. If they are equal, it returns 0 which is numerically equivalent to the false boolean. Thus you need to modify your if statements like this:
if(!strcmp(argv[1],name)){
printf("name_key = 1;");
name_key = 1;
}else if(!strcmp(argv[1],key)){
printf("name_key = 2;");
name_key = 2;
}
if (!strcmp(argv[2],cs)){
printf("cs_ci = 1;");
cs_ci = 1;
}else if(!strcmp(argv[2],ci)){
printf("cs_ci = 2;");
cs_ci = 2;
}
if (!strcmp(argv[3],ever)){
printf("ever_once = 1;");
ever_once = 1;
}else if(!strcmp(argv[3],once)){
printf("ever_once = 2;");
ever_once = 2;
}
This way if the two strings are equivalent alphabetically, then strcmp will return 0 which will be negated to assume the value of true and the program execution will enter the if block. If the two strings are NOT equal, then strcmp will return either a positive or a negative value which is equivalent to the boolean true. Negating this will assume the value of false and the program execution will not enter the if block.
Hope this helps!
EDIT:
To help you understand why your to_lowercase function might not be working properly, you have to understand two things about arrays:
Firstly, the array contents are not passed to the function. The C language (and thus all compilers/interpreters) only passes the single value of any variable to a function when it is called. In this case, the array only contains the memory address of the first element of the array. That address is passed to the function instead of the array as a whole.
Secondly, an array declaration is simply a pointer to the first location of a contiguous set of locations in memory. When you reference the first element of an array you are accessing the first memory location pointed to by the pointer. When you reference the second, third, or even fourth element of the array, you are accessing the locations in memory that are exactly 2 or 3 or even 4 memory locations away from the first element.
So what can we do with this? What happens when we change the values pointed to by the pointer? Remember, when you modify an array, (without intervention of some memory allocation tricks) the memory locations pointed to by the pointer do not change. Only the values in those memory locations change. This is why we cannot copy arrays by simply typing: array1 = array2. All this is doing is copying the pointer in array2 to array1. Any modifications you make to array2 will have a side effect of also being made in array1 because they both now point to the same locations in memory!
So if you think you have an error in your to_lowercase function, try this modification to it. I cannot test your complete code because my GCC compiler is for Linux, but it should work for you:
void to_lowercase(char *str)
{
for(int i = 0; str[i]; i++)
{
str[i] = tolower(str[i]);
}
}
HOW DOES THIS WORK?
When we pass an array to a function, we are passing the value of the array pointer. Thus the function only receives a location in memory. By utilizing this pointer, we can advance through memory to the other locations pointed to by that pointer (via str[i]) and modify them freely without affecting the value of the pointer itself.
Hope this helps.

You're correct - your to_lowercase function is doing multiple things wrong:
The way this function is currently written newStr is defining an array of char *, not an array of char.
You're then returning newStr, which is an automatic variable defined on the stack. As soon as the function returns newStr is subject to being overwritten by any other subroutine calls which are made.
You could choose to define to_lowercase as
const char * to_lowercase(char* str){
char *newStr = malloc(strlen(str)+1);
for(int i = 0; str[i]; i++)
newStr[i] = tolower(str[i]);
return newStr;
}
but this means that you'd have to be careful to free the memory allocated in to_lowercase each time you use it.
A better idea might be to write to_lowercase in such a manner that it changes the string it's passed to lower case in-place:
char *to_lowercase(char* str){
for(char *p = str; *p; p++)
*p = tolower(*p);
return str;
}
This overwrites the original content of str so you'll need to be sure to account for that in your code.

Related

Cannot access empty string from array of strings in C

I'm using an array of strings in C to hold arguments given to a custom shell. I initialize the array of buffers using:
char *args[MAX_CHAR];
Once I parse the arguments, I send them to the following function to determine the type of IO redirection if there are any (this is just the first of 3 functions to check for redirection and it only checks for STDIN redirection).
int parseInputFile(char **args, char *inputFilePath) {
char *inputSymbol = "<";
int isFound = 0;
for (int i = 0; i < MAX_ARG; i++) {
if (strlen(args[i]) == 0) {
isFound = 0;
break;
}
if ((strcmp(args[i], inputSymbol)) == 0) {
strcpy(inputFilePath, args[i+1]);
isFound = 1;
break;
}
}
return isFound;
}
Once I compile and run the shell, it crashes with a SIGSEGV. Using GDB I determined that the shell is crashing on the following line:
if (strlen(args[i]) == 0) {
This is because the address of arg[i] (the first empty string after the parsed commands) is inaccessible. Here is the error from GDB and all relevant variables:
(gdb) next
359 if (strlen(args[i]) == 0) {
(gdb) p args[0]
$1 = 0x7fffffffe570 "echo"
(gdb) p args[1]
$2 = 0x7fffffffe575 "test"
(gdb) p args[2]
$3 = 0x0
(gdb) p i
$4 = 2
(gdb) next
Program received signal SIGSEGV, Segmentation fault.
parseInputFile (args=0x7fffffffd570, inputFilePath=0x7fffffffd240 "") at shell.c:359
359 if (strlen(args[i]) == 0) {
I believe that the p args[2] returning $3 = 0x0 means that because the index has yet to be written to, it is mapped to address 0x0 which is out of the bounds of execution. Although I can't figure out why this is because it was declared as a buffer. Any suggestions on how to solve this problem?
EDIT: Per Kaylum's comment, here is a minimal reproducible example
#include<stdio.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>
#include <sys/stat.h>
#include<readline/readline.h>
#include<readline/history.h>
#include <fcntl.h>
// Defined values
#define MAX_CHAR 256
#define MAX_ARG 64
#define clear() printf("\033[H\033[J") // Clear window
#define DEFAULT_PROMPT_SUFFIX "> "
char PROMPT[MAX_CHAR], SPATH[1024];
int parseInputFile(char **args, char *inputFilePath) {
char *inputSymbol = "<";
int isFound = 0;
for (int i = 0; i < MAX_ARG; i++) {
if (strlen(args[i]) == 0) {
isFound = 0;
break;
}
if ((strcmp(args[i], inputSymbol)) == 0) {
strcpy(inputFilePath, args[i+1]);
isFound = 1;
break;
}
}
return isFound;
}
int ioRedirectHandler(char **args) {
char inputFilePath[MAX_CHAR] = "";
// Check if any redirects exist
if (parseInputFile(args, inputFilePath)) {
return 1;
} else {
return 0;
}
}
void parseArgs(char *cmd, char **cmdArgs) {
int na;
// Separate each argument of a command to a separate string
for (na = 0; na < MAX_ARG; na++) {
cmdArgs[na] = strsep(&cmd, " ");
if (cmdArgs[na] == NULL) {
break;
}
if (strlen(cmdArgs[na]) == 0) {
na--;
}
}
}
int processInput(char* input, char **args, char **pipedArgs) {
// Parse the single command and args
parseArgs(input, args);
return 0;
}
int getInput(char *input) {
char *buf, loc_prompt[MAX_CHAR] = "\n";
strcat(loc_prompt, PROMPT);
buf = readline(loc_prompt);
if (strlen(buf) != 0) {
add_history(buf);
strcpy(input, buf);
return 0;
} else {
return 1;
}
}
void init() {
char *uname;
clear();
uname = getenv("USER");
printf("\n\n \t\tWelcome to Student Shell, %s! \n\n", uname);
// Initialize the prompt
snprintf(PROMPT, MAX_CHAR, "%s%s", uname, DEFAULT_PROMPT_SUFFIX);
}
int main() {
char input[MAX_CHAR];
char *args[MAX_CHAR], *pipedArgs[MAX_CHAR];
int isPiped = 0, isIORedir = 0;
init();
while(1) {
// Get the user input
if (getInput(input)) {
continue;
}
isPiped = processInput(input, args, pipedArgs);
isIORedir = ioRedirectHandler(args);
}
return 0;
}
Note: If I forgot to include any important information, please let me know and I can get it updated.
When you write
char *args[MAX_CHAR];
you allocate room for MAX_CHAR pointers to char. You do not initialise the array. If it is a global variable, you will have initialised all the pointers to NULL, but you do it in a function, so the elements in the array can point anywhere. You should not dereference them before you have set the pointers to point at something you are allowed to access.
You also do this, though, in parseArgs(), where you do this:
cmdArgs[na] = strsep(&cmd, " ");
There are two potential issues here, but let's deal with the one you hit first. When strsep() is through the tokens you are splitting, it returns NULL. You test for that to get out of parseArgs() so you already know this. However, where your program crashes you seem to have forgotten this again. You call strlen() on a NULL pointer, and that is a no-no.
There is a difference between NULL and the empty string. An empty string is a pointer to a buffer that has the zero char first; the string "" is a pointer to a location that holds the character '\0'. The NULL pointer is a special value for pointers, often address zero, that means that the pointer doesn't point anywhere. Obviously, the NULL pointer cannot point to an empty string. You need to check if an argument is NULL, not if it is the empty string.
If you want to check both for NULL and the empty string, you could do something like
if (!args[i] || strlen(args[i]) == 0) {
If args[i] is NULL then !args[i] is true, so you will enter the if body if you have NULL or if you have a pointer to an empty string.
(You could also check the empty string with !(*args[i]); *args[i] is the first character that args[i] points at. So *args[i] is zero if you have the empty string; zero is interpreted as false, so !(*args[i]) is true if and only if args[i] is the empty string. Not that this is more readable, but it shows again the difference between empty strings and NULL).
I mentioned another issue with the parsed arguments. Whether it is a problem or not depends on the application. But when you parse a string with strsep(), you get pointers into the parsed string. You have to be careful not to free that string (it is input in your main() function) or to modify it after you have parsed the string. If you change the string, you have changed what all the parsed strings look at. You do not do this in your program, so it isn't a problem here, but it is worth keeping in mind. If you want your parsed arguments to survive longer than they do now, after the next command is passed, you need to copy them. The next command that is passed will change them as it is now.
In main
char input[MAX_CHAR];
char *args[MAX_CHAR], *pipedArgs[MAX_CHAR];
are all uninitialized. They contain indeterminate values. This could be a potential source of bugs, but is not the reason here, as
getInput modifies the contents of input to be a valid string before any reads occur.
pipedArgs is unused, so raises no issues (yet).
args is modified by parseArgs to (possibly!) contain a NULL sentinel value, without any indeterminate pointers being read first.
Firstly, in parseArgs it is possible to completely fill args without setting the NULL sentinel value that other parts of the program should rely on.
Looking deeper, in parseInputFile the following
if (strlen(args[i]) == 0)
contradicts the limits imposed by parseArgs that disallows empty strings in the array. More importantly, args[i] may be the sentinel NULL value, and strlen expects a non-NULL pointer to a valid string.
This termination condition should simply check if args[i] is NULL.
With
strcpy(inputFilePath, args[i+1]);
args[i+1] might also be the NULL sentinel value, and strcpy also expects non-NULL pointers to valid strings. You can see this in action when inputSymbol is a match for the final token in the array.
args[i+1] may also evaluate as args[MAX_ARGS], which would be out of bounds.
Additionally, inputFilePath has a string length limit of MAX_CHAR - 1, and args[i+1] is (possibly!) a dynamically allocated string whose length might exceed this.
Some edge cases found in getInput:
Both arguments to
strcat(loc_prompt, PROMPT);
are of the size MAX_CHAR. Since loc_prompt has a length of 1. If PROMPT has the length MAX_CHAR - 1, the resulting string will have the length MAX_CHAR. This would leave no room for the NUL terminating byte.
readline can return NULL in some situations, so
buf = readline(loc_prompt);
if (strlen(buf) != 0) {
can again pass the NULL pointer to strlen.
A similar issue as before, on success readline returns a string of dynamic length, and
strcpy(input, buf);
can cause a buffer overflow by attempting to copy a string greater in length than MAX_CHAR - 1.
buf is a pointer to data allocated by malloc. It's unclear what add_history does, but this pointer must eventually be passed to free.
Some considerations.
Firstly, it is a good habit to initialize your data, even if it might not matter.
Secondly, using constants (#define MAX_CHAR 256) might help to reduce magic numbers, but they can lead you to design your program too rigidly if used in the same way.
Consider building your functions to accept a limit as an argument, and return a length. This allows you to more strictly track the sizes of your data, and prevents you from always designing around the maximum potential case.
A slightly contrived example of designing like this. We can see that find does not have to concern itself with possibly checking MAX_ARGS elements, as it is told precisely how long the list of valid elements is.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define MAX_ARGS 100
char *get_input(char *dest, size_t sz, const char *display) {
char *res;
if (display)
printf("%s", display);
if ((res = fgets(dest, sz, stdin)))
dest[strcspn(dest, "\n")] = '\0';
return res;
}
size_t find(char **list, size_t length, const char *str) {
for (size_t i = 0; i < length; i++)
if (strcmp(list[i], str) == 0)
return i;
return length;
}
size_t split(char **list, size_t limit, char *source, const char *delim) {
size_t length = 0;
char *token;
while (length < limit && (token = strsep(&source, delim)))
if (*token)
list[length++] = token;
return length;
}
int main(void) {
char input[512] = { 0 };
char *args[MAX_ARGS] = { 0 };
puts("Welcome to the shell.");
while (1) {
if (get_input(input, sizeof input, "$ ")) {
size_t argl = split(args, MAX_ARGS, input, " ");
size_t redirection = find(args, argl, "<");
puts("Command parts:");
for (size_t i = 0; i < redirection; i++)
printf("%zu: %s\n", i, args[i]);
puts("Input files:");
if (redirection == argl)
puts("[[NONE]]");
else for (size_t i = redirection + 1; i < argl; i++)
printf("%zu: %s\n", i, args[i]);
}
}
}

After creating an array through dynamic allocation, a problem occurs when changing the memory size through realloc in C

I am practicing C language.
I wanted to use dynamic allocation to use only the size of the string I input as memory and check whether the input string was properly saved.
So, I wrote the following code using malloc and realloc functions.
#define _CRT_SECURE_NO_WARNINGS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void str_copy(char* str_array_f) {
void* tmp;
char buf;
unsigned char arr_size = 1;
unsigned char arr_cur = 0;
while ((buf = getchar())) {
if (buf == '\n') {
break;
}
str_array_f[arr_cur++] = (char)buf;
tmp = realloc(str_array_f, ((arr_size++) * sizeof(char)) + sizeof(char));
if (tmp != 0) {
str_array_f = tmp;
}
else {
printf("memory leak error occur! \n");
break;
}
}
str_array_f[arr_size - 1] = 0x00;
}
void main() {
int contiune = 1;
while (contiune) {
char* str_array = malloc(sizeof(char) + sizeof(char));
printf("Please type something : ");
str_copy(str_array);
printf("'str_array' have this : %s \n", str_array);
printf("-------------------------------------------------\n");
if (str_array[0] == '1') {
contiune = 0;
}
free(str_array);
}
}
And, as a result of the performance,
The following problems have occurred.
Strange values sometimes appear from the 5th character of the intermittently printed value
(To reproduce this issue, it is recommended to remove the while loop and try repeatedly)
In the case of repeatedly receiving a value by using the while loop, an error occurs after 4 repetitions.
If the allocated memory of tmp, which is a void type pointer, is released after line 22(e.g., 'free(tmp);'), when executed, no output and an error occurs immediately.
For the above 3 problems, I am not sure what is the cause and how to fix it.
Please let me know if there is a solution.
And, if there is a bad coding method in my code in terms of efficiency or various aspects, I would appreciate it if you let me know.
*Programming execution environment : Visual studio 2019
to explain what you're doing wrong I'm going to use a minimal example here
void change_x(int x) {
x = 2;
}
int main() {
int x = 1;
change_x(x);
printf("%i\n", x); // it'll print 1 not 2
return 0;
}
here the integer x is copied when the function is called and changing it won't really change the x in main. similarly you are doing in your code that str_array_f = tmp; it really won't change the str_array but the copied value. and you're trying to free a pointer that was reallocated before.
the fix for the example above is not to pass the value x instead pass the address of x (which is equivalent to pass by reference in other languages)
void change_x(int* x) {
*x = 2;
}
int main() {
int x = 1;
change_x(&x);
printf("%i\n", x); // it'll print 1 not 2
return 0;
}
and for your code
void str_copy(char** str_array_f) {...} // change the parameter
*str_array_f = tmp; // de reference and use it.
str_copy(&str_array); // call with it's address
And one more thing, don't reallocate more often it's not efficient. instead just just allocate your "array" type with a minimum size and when it's filled reallocate it with the size of 2 times of it (or 1.5 if you like)

Clear char array in C without any standard library

I'm working on a class project that would require me to make unique strings and I want to concatenate a number to a string. However I do NOT have access to C Standard Library (memset, malloc, etc.). I made this which works:
char* concat(char* name, int num) {
int i, j;
char newName[50], stack[5];
for(i=0; name[i]!='\0'; ++i) {
newName[i] = name[i];
}
for (j=0; num>=1 || num==0; j++) {
stack[j] = (num % 10) + '0';
num = num / 10;
if (num==0) break;
}
while (j>=0) {
newName[i++] = stack[j--];
}
name[0] = '\0';
return newName;
}
But then as I tested it with multiple strings, I realized that newName was being reused over and over. For ex.
This test file outputs the following:
int main() {
char* rebecca = concat("rebecca", 1);
char* bill = concat("bill", 2);
Write(rebecca); /* bill2ca1 */
Write(bill); /* bill2ca1 */
}
It successfully appends the 1 to rebecca, but then when I call concat on bill, it overwrites the first 5 letter but keeps the same chars from before in newName.
QUESTION: How to clear a char array so the next time it's called it will be set to empty, or dynamically allocate it (without using C Standard Library)?
Without using malloc, you can simply put the memory on the stack of the calling function, to keep in the scope where it is needed. It's easier to add the buffer pointer to the argument list like so:
char* concat(char *newName, char* name, int num) {
int i, j;
char stack[5];
:
:
}
int main() {
char rebecca[50];
char bill[50];
concat(rebecca, "rebecca", 1);
concat(bill, "bill", 2);
write(rebecca);
write(bill);
}
Generally speaking, assign memory where it will be used. Embedded programming (which might need to run for months without a reboot) avoids malloc like the plague, just because of the risk of memory leaks. You then need to assign extra space since you may not know the size at compile time, and then ideally check for running past the end of the buffer. Here we know the string sizes and 50 chars is more than enough.
Edit:
The other issue is that you're not null terminating. The print will go until it hits 0x00. Your line
name[0] = '\0';
should be
newName[i] = '\0';
You've got a major issue that you're overlooking. In your function, newName is a local variable (array) and you're returning it from the function. This invokes undefined behavior. The beauty of UB is that, sometime it appears to work as expected.
You need to take a pointer and allocate memory dynamically instead, if you want to return it from your concat() function. Also, in the main(), after using it, you need to free() it.
A better alternative, maybe, if you choose to do so, is
Define the array in the caller.
Pass the array to the function.
Inside the function, memset() the array before you perform any other operation.
One thing to remember, this way, every call to the function will clean the previous result.
EDIT:
If you cannot use memset(), in the main, you can use a for loop like
for (i = 0; i < sizeof(arr)/sizeof(arr[0]); i++)
arr[i] = 0;
to clear the array before passing it on next time.
You're returning the address of a local variable. Since the variable goes out of scope when the function returns, this invokes undefined behavior.
You function should dynamically allocate memory for the result of the concatenation, then return that buffer. You'll need to be sure to free that buffer later to prevent a memory leak:
char* concat(char* name, int num) {
int i, j;
char *newName, stack[5];
// allocate enough space for the existing string and digits for a 64-bit number
newName = malloc(strlen(name) + 30);
for(i=0; name[i]!='\0'; ++i) {
newName[i] = name[i];
}
for (j=0; num>=1 || num==0; j++) {
stack[j] = (num % 10) + '0';
num = num / 10;
if (num==0) break;
}
while (j>=0) {
newName[i++] = stack[j--];
}
newName[i] = '\0';
return newName;
}
int main() {
char* rebecca = concat("rebecca", 1);
char* bill = concat("bill", 2);
Write(rebecca);
Write(bill);
free(rebecca);
free(bill);
}

Resetting a char buffer in C

I'm working on a homework assignment and I need to basically create a character buffer. One of the functions I need to create is called "b_reset". It's purpose is to reinitialize the given buffer so that it will point to the first position in the char buffer. This is needed because later on, when a new char is added to the buffer, it needs to be added to the first position in the buffer.
This is the code I have thus far:
The struct:
typedef struct BufferDescriptor {
char * ca_head ;
int capacity ;
char inc_factor;
int addc_offset ;
int mark_offset ;
char r_flag;
char mode;
} Buffer ;
The code:
int b_reset ( Buffer *pB )
{
Buffer *temp = NULL;
int i = 0;
int j = 1;
if (pB == NULL)
{
return R_FAIL_1;
}
else
{
temp = (Buffer*)malloc(sizeof(Buffer*));
if (temp == NULL)
{
return R_FAIL_1;
}
temp->ca_head = (char*)malloc(pB->capacity);
if (!temp->ca_head)
{
temp = NULL;
return R_FAIL_1;
}
for(i = 0;i < ca_getsize(pB);++i)
{
temp->ca_head[j] = pB->ca_head[i];
j++;
}
pB->ca_head = temp->ca_head;
//free(temp->ca_head);
//free(temp);
return 0;
}
}
My goal in this code was to create a temporary buffer that would basically shift over everything 1 time based on the actual given buffer. This would make the first position empty so another char could be added.
The problem I'm running into is that the original buffer doesn't seem to be returning the right values after I reset it.
When I do this for example:
temp->ca_head[0] = 'a';
temp->ca_head[1] = 'b';
temp->ca_head[2] = 'c';
temp->ca_head[3] = 'd';
temp->ca_head[4] = 'e';
b_reset(temp); //this will return the size as 0, when it's actually 5
//temp->ca_head[0] = 'i'; //if this is executed, it returns the size as 6
//and prints out the right values, but if it's not,
//it will not print out anything
printf("%d", ca_getsize(temp));
for(i = 0;i < ca_getsize(temp);++i)
{
printf("%c", temp->ca_head[i]);
}
I know something is going wrong here, but I'm not too sure what. Any suggestions would be greatly appreciated.
This code is based on your followup comment:
well I'm not trying to resize the buffer, I just want to create an
empty space in the first position, so basically shifting everything to
the right 1 time. The assumption is that there is a enough space in
the buffer to handle this process.
I don't think you need to do any malloc() ing beyond the initial one. You can just shift everything up in a loop:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#define R_FAIL_1 1
#define BUFFER_SIZE 10
typedef struct BufferDescriptor {
char * ca_head ;
int capacity ;
char inc_factor;
int addc_offset ;
int mark_offset ;
char r_flag;
char mode;
} Buffer ;
void allocate_buffer(Buffer *pB, int size)
{
pB->ca_head = malloc(size);
assert(pB->ca_head);
pB->capacity = size;
}
int ca_getsize( Buffer *pB)
{
return pB->capacity;
}
int b_reset ( Buffer *pB )
{
int i = 0;
if (pB == NULL)
{
return R_FAIL_1;
}
else
{
if ( ca_getsize(pB) <= 0 || pB->ca_head == NULL )
return R_FAIL_1;
}
// shift data up by 1 byte
for( i = ca_getsize(pB) - 1 ; i > 0;i-- )
{
pB->ca_head[i] = pB->ca_head[i-1];
}
pB->ca_head[0] = '\0';
return 0;
}
void print_buffer(Buffer *pB)
{
printf("capacity: %d \n", ca_getsize(pB));
for (int i = 0;i < ca_getsize(pB);++i)
{
printf("buffer(%d): [%d] ",i, pB->ca_head[i]);
}
printf("\n");
}
int main(void)
{
Buffer a_buffer;
allocate_buffer(&a_buffer,BUFFER_SIZE);
strcpy(a_buffer.ca_head,"abcdefgh");
print_buffer(&a_buffer);
int ret = b_reset(&a_buffer);
assert(ret == 0);
print_buffer(&a_buffer);
}
temp = (Buffer*)malloc(sizeof(Buffer*));
You need to allocate enough space to hold a Buffer, but you only allocate enough space to hold a pointer to a buffer. This should be:
temp = (Buffer*)malloc(sizeof(Buffer));
You are managing your memory incorrectly. You are allocating memory for a new Buffer struct when actually you only need to handle the memory of the ca_head member (if my interpretation of your homework problem is correct).
Each time you invoke b_reset, you will allocate memory for this struct that will not be released. If you don't handle your memory correctly, you will experience unexpected results as the one you are reporting in your question.
I suggest you to make a research on the function realloc and use it properly in your b_reset function.
Good luck with your homework.

pass array by reference

I cant figure out where I am messing up. I am passing an array of character pointers. Inside the function I am trying to use strtok to break up a string into smaller pieces to be assigned to the char * array. I can try printing it off in the function and it all shows up correctly. As soon as I try to print it back in main I just get garbage.
#include <stdio.h>
#include <string.h>
#define CMDLEN 100
#define MAXARG 5
void prompt();
int getCommand (char* cmdAndParameters[]);
int main() {
int numArgs = 0;
char* cmdAndParameters [MAXARG];
while (true){
prompt ();
numArgs = getCommand (cmdAndParameters);
}
}
void prompt() {
printf("shell> ");
}
int getCommand(char* cmdAndParameters[]){
int argNum = 0;
bool foundArg = true;
char* delimiters = " \t\n";
char userRequest[CMDLEN];
fgets(userRequest,sizeof(userRequest), stdin);
if ((cmdAndParameters[argNum] = strtok(userRequest, delimiters)) != NULL)
{
argNum++;
for (; argNum < MAXARG && foundArg; argNum++) {
if ((cmdAndParameters[argNum] = strtok(NULL,delimiters))
== NULL)
{
foundArg = false;
}
// merely to test output remove later
else {printf("%s\n", cmdAndParameters[argNum]);}
}
}
return argNum;
}
In this case, your inner array of chars is allocated "automatic", which is to say, on the stack. When you do the strtok, you're assigning a pointer to memory allocated on the stack, and then returning -- which means the memory is no longer allocated.
Move the userRequest array into file scope (ie, outside a block) or make the allocation 'static' and you'll have a better shot.
Update
Well, it's a little more than that, now that I look again.
First of all, you can clean it up considerably if you use a while loop, something like
argNum = 0;
while((cmdAndParameters[argNum++] = strtok(userRequest, delimiters)) != NULL)
; /* all the work is happening in the conditional part of the while */
or even a for loop as
for(argNum = 0;
(cmdAndParameters[argNum] = strtok(userRequest, delimiters)) != NULL);
argNum++)
; /* still all the work is in the for */
and now if argNum > 0 you know you found something.
Second, you need to think about how and when you're allocating memory. Your cmdAndParameters array is allocated when main starts (on the stack, it's "automatic") so it's around as long as your program, you're okay there. But your userRequest array is allocated auto in getCommand; when getCommand returns, the memory is deallocated; the stack pointer moves back over it and you have no guarantees any longer. So when you do the strtok, you're saving pointers into stack, which can lead to no good.
Do you want
for (; argNum < MAXARG && foundArg; argNum++)
or something like
for(argCntr = argNum; argCntr < MAXARG && foundArg; argCntr++)
Hope that helps.

Resources