The cplusplus.com documentation on getenv() states...
The pointer returned points to an internal memory block, whose content or validity may be altered by further calls to getenv
...which I take to mean, "If you want to keep the content, copy it." So, since I need to retrieve several variables, I wrote a couple of little wrapper functions:
#include <iostream>
#include <string.h>
using namespace std;
void getEnv (char *val, const char *var) {
val = nullptr;
char *enVar = getenv(var);
if (enVar != nullptr) {
val = new char[strlen(enVar) + 1];
strcpy(val, enVar);
}
}
void getEnv (int &val, const char *var) {
val = -1;
char *enVar = getenv(var);
if (enVar != nullptr) {
val = atoi(enVar);
}
}
int main() {
char *textMode = nullptr;
int cLen = 0;
getEnv(cLen, "CONTENT_LENGTH");
cout << cLen << endl << endl;
getEnv(textMode, "TEXT_MODE");
if (textMode == nullptr)
cout << "Not set.";
else
cout << "[" << textMode << "]<br>\n";
return 0;
}
The int version works as expected, but I get nothing back from the char version, and I mean nothing: if I don't initialize *textMode at declaration it remains an uninitialized pointer.
It's pointers, right? Right? I know it is. Gotta be pointers. I'll figure them out one of these days, but hey -- at least I got my linked list to work! Yay!
Your second function takes val (an int) by reference: void getEnv (int &val, const char *var) and so can modify the variable passed to it as you expect.
Your first function takes val (a char*) by value: void getEnv (char *val, const char *var) so modifying val has no affect on the variable passed to it. A simple solution is to simply take it as a reference as well: void getEnv (char *&val, const char *var)
Follow up to my comments and the OP's response to them.
Here's what I was thinking:
#include <iostream>
#include <string.h>
using namespace std;
// Use a class to encapsulate the data need to be captured
// in an environment variable.
class EnvironmentVariable
{
public:
EnvironmentVariable(char const* name) : name_(name), isSet_(false)
{
char *val = getenv(name);
if ( val != nullptr )
{
isSet_ = true;
this->value_ = val;
}
}
bool isSet() const
{
return isSet_;
}
void getValue(char const*& val) const
{
if ( isSet_ )
{
val = this->value_.c_str();
}
else
{
val = nullptr;
}
}
void getValue(int& val) const
{
if ( isSet_ )
{
val = stoi(this->value_);
}
else
{
val = 0; // Find a suitable default value
}
}
private:
std::string name_;
std::string value_;
bool isSet_;
};
int main() {
char const* textMode = nullptr;
int cLen = 0;
EnvironmentVariable env1("CONTENT_LENGTH");
env1.getValue(cLen);
cout << cLen << endl << endl;
EnvironmentVariable env2("TEXT_MODE");
env2.getValue(textMode);
if (textMode == nullptr)
cout << "Not set.\n";
else
cout << "[" << textMode << "]<br>\n";
return 0;
}
Related
I want to pass a double pointer as argument to a function, but I cant see what I am doing wrong.
#include <iostream>
#include <string>
using namespace std;
void changeString(string ***strPtr) {
strPtr = new string**[1];
*strPtr = new string*[1];
**strPtr = new string("hello");
//cout << strPtr[0][0][0];
}
int main()
{
string **strPtr;
changeString(&strPtr);
//cout << strPtr[0][0];
return 0;
}
The cout in changeString works fine, but the cout in main throws the exception read access violation. strPtr was 0xCCCCCCCC.
Your example is basically equivalent to this:
void changeString(string **strPtr) {
strPtr = new string*[1];
*strPtr = new string("hello");
//cout << strPtr[0][0];
}
int main()
{
string *strPtr;
changeString(&strPtr);
//cout << strPtr[0];
return 0;
}
And that is basically equivalent to this:
void changeString(string *strPtr)
{
strPtr = new string("hello"); // Changes the pointer, not the string!
//cout << strPtr[0];
}
int main()
{
string str;
changeString(&str);
//cout << str;
return 0;
}
At this point it should start to become obvious that you are assigning a new value to the pointer, not the pointed-to object. Consider this:
void change(SomeType t)
{
t = somethingElse;
}
int main()
{
SomeType t;
change(t); // Does not change t.
}
In your case, SomeType happens to be string* (or string***) - you are just overwriting a local variable.
To fix your code, just skip the first line in changeString:
http://coliru.stacked-crooked.com/a/88874ee3601ef853
void changeString(string ***strPtr)
{
*strPtr = new string*[1];
**strPtr = new string("hello");
cout << strPtr[0][0][0];
}
int main()
{
string **strPtr;
changeString(&strPtr);
cout << strPtr[0][0];
return 0;
}
I am writing the program for Generic Stack.SO I pass the function pointer to the function StackNew() API. When I see the address of the StringFree API, it is 0x012b2770 ,
but when I see the address of (*free) API it is 0x012B13ED
I had thought that the pointer's copy will be passed . As you can see that's not happening but the program is working as it should .
I thought that the starting address of the StringFree API is passed ,like suppose the starting address of StringFree is 0x10 then , 0x10 is passed to free API so that the free also points to starting address of StringFree so that the value of the free also will be 0x10 ,but that is not happening .Can you explain me what is going on here? Thanks.
My program :
#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<string.h>
#include<iostream>
using namespace std;
typedef struct
{
void *elems;
int loglen;
int alloclen;
int elemSize;
void (*freefn) (void*);
}Stack;
void StringFree(void* target)
{
char** s = (char**)target;
free(*s);
if (s != NULL) {
std::cout << "Not null" << std::endl;
}
s = NULL;
}
void StackNew(Stack *s, int elemSize,void (*free) (void*))
{
s->elemSize = elemSize;
s->loglen = 0;
s->alloclen = 4;
s->elems = malloc(4 * elemSize);
s->freefn = free;
//assert(s->elems != 0 );
}
void StackDispose(Stack *s)
{
if (s->freefn != 0)
{
for (int i = 0 ; i < 3 ; i++ )
{
void* source = (char*)s->elems + i*s->elemSize;
s->freefn(source);
}
}
}
void StackPush(Stack *s, void* elemAddr)
{
if (s->alloclen == s->loglen)
{
s->alloclen *= 2;
s->elems = realloc(s->elems, s->alloclen * s->elemSize);
}
void* target = (char*)s->elems + s->loglen*s->elemSize;
memcpy(target, elemAddr, s->elemSize);
s->loglen++;
}
void* StackPop(Stack *s, void* elemAddr)
{
s->loglen--;
void* source = (char*)s->elems + s->loglen * s->elemSize;
memcpy(elemAddr, source, s->elemSize);
return elemAddr;
}
int main()
{
Stack s;
std::cout << sizeof(s.freefn) << std::endl;
const char* friends[] = { "AlexJonesisabitchofjoerogan" , "Bob" , "Carl"};
StackNew(&s, sizeof(char*),StringFree);
for (int i = 0; i < 3; i++)
{
//int* cc = (int *)_strdup(friends[i]);
char* copy = _strdup(friends[i]);
std::cout << copy << std:: endl;
StackPush(&s, ©);
}
char* name;
for (int i = 0; i < 3; i++)
{
StackPop(&s, &name);
printf("%s\n", name);
// free()
}
StackDispose(&s);
std::cin.get();
return 0;
}
Pointers are still passed by value, so when you pass a pointer, it copies the value of the pointer as it would for an integer.
Return a pointer to the last appearance of c
appearing inside s and nullptr (0) if c does not appear inside s.
#include <string>
#include <iostream>
#include <cassert>
using namespace std;
const char* myStrRChr(const char* s, char c)
{
int curIdx = 0;
char last;
while (s[curIdx] != '\0')
{
if (s[curIdx] == c)
last = s[curIdx];
curIdx++;
}
if (s[curIdx] == c)
return last;
else
// return '\0', nullptr, NULL
return "";
}
int main()
{
char cstr[50] = "Abadabadoo!";
char buf[10];
const char * cat = "cat";
char dog[] = "Labradoodle";
cout << "\nmyStrRChr(cstr, 'a') expects adoo!" << endl;
cout << " -- " << myStrRChr(cstr, 'a') << endl;
return 0;
}
This code returns "adabadoo!". I can't wrap my mind around as to how to get the last instance of "char c."
You can do this by obtaining a pointer to the end of the string and decrementing down the string searching for the character c, and a pointer to the beginning of the string to know where to stop looping:
const char *mystrrchr(const char *str, char c)
{
int len = strlen(str);
char *p = const_cast<char *>(&str[len-1]);
char *stop = const_cast<char *>(&str[0]);
while(p>=stop)
{
if(*p==c)
{
return p;
}
p--;
}
return nullptr;
}
So I'm having a substantial amount of trouble with this one bit of code. I've included the whole program for context, but my issue lies in the cleanUp function, wherein I (attempt to) remove all characters that are not 'A' through 'Z'.
Any tips?
#include <iostream>
#include <cstdlib>
#include <string>
#include <stdio.h>
#include <ctype.h>
using namespace std;
bool again(string title); // Checks if you want to run again.
void makeUpper(char word[]);
void getReverse(char word[], char reversed[]);
char * find(char *str, char what);
bool equal(char word[], char reversed[]);
int size(char word[]);
char * cleanUp(char *str);
int main()
{
char word[256] = "Hello?? There!", reversedWord[256];
do
{
cout<<"Please enter the string to check: ";
makeUpper(word);
cout << word;
cleanUp(word);
getReverse(word,reversedWord);
if(equal(word, reversedWord))
cout<<"You have a palindrome!"<<endl;
else
cout<<"You do not have a palindrome!"<<endl;
} while(again("Do you want to do this again? "));
return 0;
}
bool again(string title)
{
string answer;
cout<<endl<<title;
getline(cin,answer);
return toupper(answer[0]) == 'Y';
}
void makeUpper(char word[])
{
char *ptr = word;
while (*ptr) {
*ptr = toupper(*ptr);
ptr++;
}
cout << "In uppercase:: " << word << endl;
}
char * cleanUp(char * astrid)
{
char *new_astrid;
for (*astrid; *astrid != '\0'; astrid++)
{
cout << "First loop";
if (isalpha(*astrid))
{
*new_astrid = *astrid;
new_astrid = ++new_astrid;
cout << "Here!";
}
}
cout << *new_astrid;
return *new_astrid;
}
void getReverse(char word[], char reversed[])
{
char *ptr_ind = find(word, '\0'), *ptr_ind_2 = reversed;
while(ptr_ind != word-1)
{
*ptr_ind_2 = *ptr_ind;
ptr_ind--;
ptr_ind_2++;
}
*ptr_ind_2 = '\0';
}
char * find(char *str, char what)
{
char *ptr = str;
while(*ptr != what && *ptr != '\0')
ptr++;
return *ptr == what ? ptr : NULL;
}
bool equal(char word[], char reverse[])
{
int total;
char * ptr;
ptr = word;
if((total = size(word)) != size(reverse))
return false;
for(char * ptr2 = reverse; *ptr != '\0' && *ptr == *ptr2; ptr++, ptr2++);
return *ptr == '\0';
}
int size(char word[])
{
int total = 0;
char * ptr = word;
while(*ptr != '\0') //while(!ptr)
{
ptr++;
total++;
}
return total;
}
There are several errors in your code.
new_astrid is not initialized and when you call *new_astrid = *astrid you try to copy a character to uninitialized memory, which will crash the program.
You also return the dereferenced pointer to new_astrid but the function prototype of cleanUp says that you return a pointer to char.
You should initialize new_astrid with new char[strlen(astrid)]. But then your code will result in memory leaks, since you increase the pointer (new_astid = ++new_astrid). So you should store the pointer first, so you can delete it later.
Instead of using raw pointers, i would suggest you use std::strings.
My suggestion for a palindrome tester would be:
#include <iostream>
#include <string>
#include <locale>
bool isPalindrome(std::string word)
{
std::locale loc;
for (std::string::size_type i = 0; i < word.length() / 2 + 1; ++i)
{
if (std::toupper(word[i],loc) != std::toupper(word[word.length() - i - 1],loc))
{
return false;
}
}
return true;
}
int main(int , char **)
{
std::string str = "Abba";
//Remove all non alpha values from string
str.erase(std::remove_if(str.begin(), str.end(), [](char const c){return !std::isalpha(c);}), str.end());
if (isPalindrome(str) == false)
{
std::cout << str << " is no palindrome" << std::endl;
}
else
{
std::cout << str << " is a palindrome" << std::endl;
}
return 0;
}
The erasion of non alpha values in the string is from this question.
I've tried many ways to do that, I got a void which is static and is on Console class i made, Void it self works fine:
Console::WriteLine(const char* msg)
On the other side, I got another const char* non static void which calls the Console::WriteLine void from It, I've been working on C# for around a year and on C# I can easily do something like this:
string a = "Start ";
string b = a + "End";
When i call this on C++, it gives me bunch of errors:
Player::Kill(const char* Message)
{
Console::WriteLine("> " + Message + " <");
}
I've also tried the strcat thing and put, But it tells me to use strcat_s which doesn't really work, And also I've tried to do string instead of const char*, And tried char*, But all of them give errors for the thing I'm trying to do.
"const" means "cannot be changed(*1)". So you cannot simply "add" one const char string to another (*2). What you can do is copy them into a non-const character buffer.
const char* a = ...;
const char* b = ...;
char buffer[256]; // <- danger, only storage for 256 characters.
strncpy(buffer, a, sizeof(buffer));
strncat(buffer, b, sizeof(buffer));
// now buffer has the two strings joined together.
Your attempt to use std::string failed for a similar reason. You said:
std::string a = "Start";
std::string b = a + " End";
This translates to
b = (std::string)a + (const char*)" End";
Which should be ok except that it creates an extra string, what you probably wanted is
std::string a = "Start";
a += " End";
If you are getting compile errors doing this, please post them (Make sure you #include ).
Or you could do something like:
std::string addTwoStrings(const std::string& a, const std::string& b)
{
return a + b; // works because they are both strings.
}
All of the following work: (see live demo http://ideone.com/Ytohgs)
#include <iostream>
#include <string>
std::string addTwoStrings(const std::string& a, const std::string& b)
{
return a + b; // works because they are both strings.
}
void foo(const char* a, const char* b)
{
std::string str = a;
std::cout << "1st str = [" << str << "]" << std::endl;
str += " ";
std::cout << "2nd str = [" << str << "]" << std::endl;
str += b;
std::cout << "3rd str = [" << str << "]" << std::endl;
str = addTwoStrings(a, " ");
std::cout << "4th str = [" << str << "]" << std::endl;
str = addTwoStrings(str, b);
std::cout << "5th str = [" << str << "]" << std::endl;
}
int main()
{
foo("hello", "world");
}
*1 Or more accurately, "cannot be changed in-situ" - you can use it in expressions, etc, so for example, e.g.
const size_t len = strlen("hello");
size_t newLen = len + strlen("world");
// but this would not be legal:
len += 2; // error: len is const.
2 "const char a + const char* b" is actually trying to add two pointers not two strings, the result would be the address of string a plus the address of string b, the sum of which would be some random memory location
char * is a pointer (so are "> " and " <"), you cannot add pointers together.
However you can concatenate C++ strings using the + operator:
Player::Kill(const std::string& Message)
{
Console::WriteLine(("> " + Message + " <").c_str());
}
Instead of concatenating the strings and creating an extra temporary object, why not just output the 3 strings separately?
Player::Kill(const char* Message)
{
Console::Write("> ");
Console::Write(Message);
Console::WriteLine(" <");
}
Since you say it's C++ code, just just this:
void Player::Kill(std::string const& Message)
{
Console::WriteLine(("> " + Message + " <").c_str());
}
Ideally, your Console::WriteLine() is declared to also take a std::string const& in which case you don't need to do the .c_str()-dance.
#include <iostream>
using namespace std;
string string1 = "John";
string string2 = "Smith";
float string1Len = string1.length();
float combinedLen = string1.length() + string2.length();
char combine[string2.length() + string1.length()];
for(int i = 0; i < string1Len; ++i){
combine[i] = string1[i];
}
for(int i = string1Len; i < combinedLen; ++i){
combine[i] = string2[i - string1Len];
}
const char * combined = combine;
well, it is possible to make changes with const char* ,even thought it is const we cannot change its value but can change its address ,check below class for reference
class String
{
private:
const char* m_Name;
int m_Size;
public:
String() :m_Name(" "), m_Size(0) { m_Size = 0; }
String(const char* name , int size) :m_Name(name),m_Size(size) {}
String(const char* name) :m_Name(name) {}
void Display()
{
LOG(m_Name);
}
int GetSize()
{
while (m_Name[m_Size] != '\0')
{
m_Size++;
}
return m_Size;
}
void Append(const char* nameToAppend)
{
//Create an empty char array pointer "*tempPtr" of size = existing const
//char name pointer "m_Name" + const char pointer appending name
//"*nameExtention"
//this array can store both the names
int totalSize = 0;
int nameToAppendSize = 0, existingNameSize = 0;
while (nameToAppend[nameToAppendSize] != '\0')
{nameToAppendSize++;}
existingNameSize = this->GetSize();
totalSize = nameToAppendSize + existingNameSize;
char* tempPtr = new char[totalSize+1];
//Add existing const char name pointer "*m_Name" + const char pointer
//appending name "*nameExtention" to tempPtr, using a loop
int currentSize = 0;
for (int i = 0; i < existingNameSize; i++)
{
tempPtr[currentSize] = m_Name[i];
currentSize++;
}
for (int i = 0; i <= nameToAppendSize; i++)
{
if (i == nameToAppendSize)
{
tempPtr[currentSize] = '\0'; // this line tells compiler to stop
// writting inside tempPtr
}
else
{
tempPtr[currentSize] = nameToAppend[i];
currentSize++;
}
}
//--------------------------------
//Now change the address of your const char* with tempPtr
//This will change the contents of the const char*
//--------------------------------
m_Name = (char*)tempPtr;
}
};